Logstash StackOverflow error due to large plug-in configuration file

This topic describes how to fix the error that you might get due to a large Universal Connector configuration file.

When you configure more than ten Universal Connectors on one collector, you might get the following StackOverflow error:
FATAL Logstash:109 - uncaught error (in thread Ruby-0-Thread-35: /usr/share/logstash/logstash-core/lib/logstash/java_pipeline.rb:289)
java.lang.StackOverflowError: null
    at org.logstash.config.ir.expression.EventValueExpression.toString(org/logstash/config/ir/expression/EventValueExpression.java:51) ~[logstash-core.jar:?]
    at java.lang.StringConcatHelper.stringOf(java/lang/StringConcatHelper.java:453) ~[?:?]
    at org.logstash.config.ir.expression.unary.Truthy.toRubyString(org/logstash/config/ir/expression/unary/Truthy.java:36) ~[logstash-core.jar:?]
    at org.logstash.config.ir.compiler.EventCondition$Compiler.buildCondition(org/logstash/config/ir/compiler/EventCondition.java:112) ~[logstash-core.jar:?]
    at org.logstash.config.ir.CompiledPipeline$CompiledExecution.lambda$compileDependencies$6(org/logstash/config/ir/CompiledPipeline.java:546) ~[logstash-core.jar:?]
    at java.util.stream.ReferencePipeline$3$1.accept(java/util/stream/ReferencePipeline.java:197) ~[?:?] 
To avoid the stack overflow error, you can aggregate all connections and use one of the following optimized options.
Option 1
If you are configuring all the Universal Connectors from the same database type but different account IDs.
Consider the following existing workflow with three different connectors that have the same logic:
postgresql1:
 input { cloudwatch_logs { log_group=>"postgresql1", type=>"postgresql1" }
filter {if type="postgresql1" {...} }
		
postgresql2:
input { cloudwatch_logs { log_group=>"postgresql2", type=>"postgresql2" }
filter {if type="postgresql2" {...} }
		
postgresql3:
input { cloudwatch_logs { log_group=>"postgresql3", type=>"postgresql3" }

filter {if type="postgresql2" {...} }
Following is the optimized workflow with 3 different connectors that have the same logic:
input {
cloudwatch_logs { log_group=>"postgresql1", type=>"postgresql" }
cloudwatch_logs { log_group=>"postgresql2", type=>"postgresql" }
cloudwatch_logs { log_group=>"postgresql3", type=>"postgresql" }
}
filter {if type="postgresql" {...} }
See the following example of CloudWatch plug-in config file with three different accounts, ACCOUNT_ID_1>, ACCOUNT_ID_2> and <ACCOUNT_ID_3>.
cloudwatch_logs {
log_group => ["<LOG_GROUP>"]	
start_position => "end"		
access_key_id => "<ACCESS_KEY_1>"		
secret_access_key => "<SECRET_KEY_1>"		
region => "<REGION_1>" 
interval => 2
event_filter => ""
codec => multiline {          
pattern => "(((?<ts>[^[A-Z]{3}]*)UTC:(?<client_ip>[^:]*):(?<db_user>[^@]*)@(?<db_name>[^:]*):(?<session_id>[^:*]*):(?<logger>LOCATION|DETAIL|STATEMENT|HINT):%{GREEDYDATA:sql_full_log})|(^\s))"          
negate => false          
what => "previous"       
}		
type => "postgres"		
add_field => {"account_id" => "<ACCOUNT_ID_1>"}        
} 


cloudwatch_logs { 
log_group => ["<LOG_GROUP>"]
start_position => "end" 
access_key_id => "<ACCESS_KEY_2>" 
secret_access_key => "<SECRET_KEY_2>" 
region => "<REGION_2>" 
interval => 2 
event_filter => "" 
codec => multiline { 
pattern => "(((?<ts>[^[A-Z]{3}]*)UTC:(?<client_ip>[^:]*):(?<db_user>[^@]*)@(?<db_name>[^:]*):(?<session_id>[^:*]*):(?<logger>LOCATION|DETAIL|STATEMENT|HINT):%{GREEDYDATA:sql_full_log})|(^\s))" negate => false 
what => "previous" 
} 
type => "postgres" 
add_field => {"account_id" => "<ACCOUNT_ID_2>"} 
} 


cloudwatch_logs { 
log_group => ["<LOG_GROUP>"] 
start_position => "end" 
access_key_id => "<ACCESS_KEY_3>" 
secret_access_key => "<SECRET_KEY_3>" 
region => "<REGION_3>" 
interval => 2 
event_filter => "" 
codec => multiline { 
pattern => "(((?<ts>[^[A-Z]{3}]*)UTC:(?<client_ip>[^:]*):(?<db_user>[^@]*)@(?<db_name>[^:]*):(?<session_id>[^:*]*):(?<logger>LOCATION|DETAIL|STATEMENT|HINT):%{GREEDYDATA:sql_full_log})|(^\s))" negate => false 
what => "previous"
} 
type => "postgres" 
add_field => {"account_id" => "<ACCOUNT_ID_3>"}
} 
Options 2
If you are configuring all the Universal Connectors from the same database type, within the same database account and with same account ID.
Consider the following existing workflow with three different connectors that have the same logic:
postgresql1:
 input { cloudwatch_logs { log_group=>"postgresql1", type=>"postgresql1" }
filter {if type="postgresql1" {...} }
		
postgresql2:
input { cloudwatch_logs { log_group=>"postgresql2", type=>"postgresql2" }
filter {if type="postgresql2" {...} }
		
postgresql3:
input { cloudwatch_logs { log_group=>"postgresql3", type=>"postgresql3" }

filter {if type="postgresql2" {...} }
Following is the optimized workflow with 3 different connectors that have the same logic:
input { cloudwatch_logs { log_group=>"postgresql1, postgresql2, postgresql3", type=>"postgresql" }

filter {if type="postgresql" {...} } 
See the following xxample of CloudWatch plug-in config file with same account ID.
cloudwatch_logs {
log_group => ["<LOG_GROUP_1>", <LOG_GROUP_2>", <LOG_GROUP_3>"]  
#e.g. ["/aws/rds/instance/database-1/postgresql", "/aws/rds/instance/database-2/postgresql", "/aws/rds/instance/database-3/postgresql"]		
start_position => "end"		
access_key_id => "<ACCESS_KEY>"		
secret_access_key => "<SECRET_KEY>"		
region => "<REGION>" 
interval => 2
event_filter => ""
codec => multiline {          
pattern => "(((?<ts>[^[A-Z]{3}]*)UTC:(?<client_ip>[^:]*):(?<db_user>[^@]*)@(?<db_name>[^:]*):(?<session_id>[^:*]*):(?<logger>LOCATION|DETAIL|STATEMENT|HINT):%{GREEDYDATA:sql_full_log})|(^\s))"          
negate => false          
what => "previous"       
}		
type => "postgres"		
add_field => {"account_id" => "<ACCOUNT_ID>"}        
}