diff --git a/box.json b/box.json index 264f2d8..088efed 100644 --- a/box.json +++ b/box.json @@ -50,9 +50,9 @@ "build:module":"task run taskFile=build/Build.cfc :projectName=`package show slug` :version=`package show version`", "build:docs":"task run taskFile=build/Build.cfc target=docs :projectName=`package show slug` :version=`package show version`", "release":"recipe build/release.boxr", - "format":"cfformat run models,ModuleConfig.cfc --overwrite", - "format:watch":"cfformat watch models,ModuleConfig.cfc ./.cfformat.json", - "format:check":"cfformat check models,ModuleConfig.cfc", + "format":"cfformat run tests/specs,models,ModuleConfig.cfc --overwrite", + "format:watch":"cfformat watch tests/specs,models,ModuleConfig.cfc ./.cfformat.json", + "format:check":"cfformat check tests/specs,models,ModuleConfig.cfc", "install:dependencies":"install --force && cd test-harness && install --force", "start:elasticsearch7":"!docker run --name cbelasticsearch-es8 -d -p '9200:9200' -e 'discovery.type=single-node' -e 'xpack.security.enabled=false' elasticsearch:7.17.2", "start:elasticsearch":"!docker run --name cbelasticsearch-es8 -d -p '9200:9200' -e 'discovery.type=single-node' -e 'xpack.security.enabled=false' elasticsearch:8.4.2" diff --git a/models/SearchBuilder.cfc b/models/SearchBuilder.cfc index 2da6591..2eddd9c 100644 --- a/models/SearchBuilder.cfc +++ b/models/SearchBuilder.cfc @@ -1378,13 +1378,10 @@ component accessors="true" { * Append a search-time (runtime) mapping to the search. * * @name Name of the runtime mapping field - * + * * @script Script to use. `{ "script" : { "lang": "painless", "source" : } }` */ - public SearchBuilder function addRuntimeMapping( - required string name, - struct script - ){ + public SearchBuilder function addRuntimeMapping( required string name, struct script ){ if ( isNull( variables.runtimeMappings ) ) { variables.runtimeMappings = {}; } diff --git a/models/logging/AppenderService.cfc b/models/logging/AppenderService.cfc index c1f9d02..5bee52d 100644 --- a/models/logging/AppenderService.cfc +++ b/models/logging/AppenderService.cfc @@ -1,43 +1,43 @@ -component accessors="true" singleton{ - property name="logbox" inject="logbox"; - property name="elasticsearchClient" inject="Client@cbelasticsearch"; - property name="util" inject="Util@cbelasticsearch"; - property name="detachedAppenders"; - - this.logLevels = new coldbox.system.logging.LogLevels(); - - function init(){ - variables.detachedAppenders = []; - return this; - } - - /** - * Create a detached appender for use in ad-hoc logging - * - * @name The name of the appender - * @properties - * @class The class to use for the appender. Defaults to LogStashAppender - */ - public void function createDetachedAppender( - required string name, - struct properties = {}, - string class = "cbelasticsearch.models.logging.LogStashAppender" - ){ - - structAppend( - arguments.properties, - { +component accessors="true" singleton { + + property name="logbox" inject="logbox"; + property name="elasticsearchClient" inject="Client@cbelasticsearch"; + property name="util" inject="Util@cbelasticsearch"; + property name="detachedAppenders"; + + this.logLevels = new coldbox.system.logging.LogLevels(); + + function init(){ + variables.detachedAppenders = []; + return this; + } + + /** + * Create a detached appender for use in ad-hoc logging + * + * @name The name of the appender + * @properties + * @class The class to use for the appender. Defaults to LogStashAppender + */ + public void function createDetachedAppender( + required string name, + struct properties = {}, + string class = "cbelasticsearch.models.logging.LogStashAppender" + ){ + structAppend( + arguments.properties, + { // The data stream name to use for this appenders logs - "dataStreamPattern" : "logs-coldbox-#lcase( arguments.name )#*", - "dataStream" : "logs-coldbox-#lcase( arguments.name )#", - "retentionDays" : 365, + "dataStreamPattern" : "logs-coldbox-#lCase( arguments.name )#*", + "dataStream" : "logs-coldbox-#lCase( arguments.name )#", + "retentionDays" : 365, // The max shard size at which the hot phase will rollover data - "rolloverSize" : "50gb" + "rolloverSize" : "50gb" }, - false - ) + false + ) - variables.logBox.registerAppender( + variables.logBox.registerAppender( name = arguments.name, class = arguments.class, // Turn this appender off for all other logging, as it is intended to be used ad-hoc @@ -46,16 +46,14 @@ component accessors="true" singleton{ properties = arguments.properties ); - variables.detachedAppenders.append( arguments.name ); - } + variables.detachedAppenders.append( arguments.name ); + } /** * Method for retrieving a LogEvent instance. */ public LogEvent function getLogEvent(){ - return new coldbox.system.logging.LogEvent( - argumentCollection = arguments - ); + return new coldbox.system.logging.LogEvent( argumentCollection = arguments ); } /** @@ -68,8 +66,8 @@ component accessors="true" singleton{ /** * Retreives a specific appender from the logbox registry - * - * @appenderName The name of the appender to retrieve + * + * @appenderName The name of the appender to retrieve */ public function getAppender( required string appenderName ){ var registry = getAppenderRegistry(); @@ -78,12 +76,12 @@ component accessors="true" singleton{ /** * Logs a message out to a specific appender - * - * @appenderName The name of the appender to log to - * @message The message to log - * @severity The severity of the message - * @extraInfo Any extra information to log - * @category The category to log the message under + * + * @appenderName The name of the appender to log to + * @message The message to log + * @severity The severity of the message + * @extraInfo Any extra information to log + * @category The category to log the message under */ public function logToAppender( required string appenderName, @@ -92,12 +90,15 @@ component accessors="true" singleton{ struct extraInfo = {}, string category ){ - if( !isNumeric( arguments.severity ) ){ - if( !this.logLevels.keyExists( arguments.severity ) ){ - throw( type = "cbelasticsearch.logging.InvalidSeverity", message = "The severity [#arguments.severity#] provided is not valid. Please provide a valid numeric serverity or one of the following levels [#this.logLevels.keyArray().toList()#]." ); - } - arguments.severity = this.logLevels[ arguments.severity ]; - } + if ( !isNumeric( arguments.severity ) ) { + if ( !this.logLevels.keyExists( arguments.severity ) ) { + throw( + type = "cbelasticsearch.logging.InvalidSeverity", + message = "The severity [#arguments.severity#] provided is not valid. Please provide a valid numeric serverity or one of the following levels [#this.logLevels.keyArray().toList()#]." + ); + } + arguments.severity = this.logLevels[ arguments.severity ]; + } var appender = getAppender( appenderName ); if ( !isNull( appender ) ) { @@ -111,77 +112,78 @@ component accessors="true" singleton{ ) ); } else { - logbox.getRootLogger().error( - "Could not find a registered appender with the name: #appenderName#. Registered appenders are: #getAppenderRegistry().keyArray().toList()#", - arguments - ); + logbox + .getRootLogger() + .error( + "Could not find a registered appender with the name: #appenderName#. Registered appenders are: #getAppenderRegistry().keyArray().toList()#", + arguments + ); } } - /** + /** * Logs a pre-formatted message or messages out to a specific appender. The messages provided should should be structs, adhering to the [Elastic Common Schema](https://www.elastic.co/guide/en/ecs/current/ecs-field-reference.html) - * - * @appenderName The name of the appender to log to - * @message The message struct or or array of messages to log + * + * @appenderName The name of the appender to log to + * @message The message struct or or array of messages to log */ public function logRawToAppender( required string appenderName, required any messages, - boolean refresh = false + boolean refresh = false ){ var appender = getAppender( appenderName ); if ( !isNull( appender ) ) { - var createOptions = { "_index" : appender.getProperty( "dataStream" ) }; - - if( !isArray( messages ) ){ - messages = [ messages ]; - } - - var inserts = messages.map( ( log ) => { - structAppend( log, getCommonFields(), false ); - variables.util.preflightLogEntry( log ); - return log; - } ); - - elasticsearchClient.processBulkOperation( - inserts.map( ( doc ) => [ - "operation" : { "create" : createOptions }, - "source" : doc - ] ), - { - "refresh" : refresh - } - ); - - } else { - logbox.getRootLogger().error( - "Could not find a registered appender with the name: #appenderName#. Registered appenders are: #getAppenderRegistry().keyArray().toList()#", - arguments + var createOptions = { "_index" : appender.getProperty( "dataStream" ) }; + + if ( !isArray( messages ) ) { + messages = [ messages ]; + } + + var inserts = messages.map( ( log ) => { + structAppend( log, getCommonFields(), false ); + variables.util.preflightLogEntry( log ); + return log; + } ); + + elasticsearchClient.processBulkOperation( + inserts.map( ( doc ) => [ + "operation": { "create" : createOptions }, + "source" : doc + ] ), + { "refresh" : refresh } ); + } else { + logbox + .getRootLogger() + .error( + "Could not find a registered appender with the name: #appenderName#. Registered appenders are: #getAppenderRegistry().keyArray().toList()#", + arguments + ); } } - /** - * Returns common log entry fields - */ - function getCommonFields(){ - return { + /** + * Returns common log entry fields + */ + function getCommonFields(){ + return { "@timestamp" : dateTimeFormat( now(), "yyyy-mm-dd'T'HH:nn:ssZZ" ), - "file" : { "path" : CGI.CF_TEMPLATE_PATH }, - "url" : { + "file" : { "path" : CGI.CF_TEMPLATE_PATH }, + "url" : { "domain" : CGI.SERVER_NAME, "path" : CGI.PATH_INFO, "port" : CGI.SERVER_PORT, "query" : CGI.QUERY_STRING, "scheme" : lCase( listFirst( CGI.SERVER_PROTOCOL, "/" ) ) }, - "http" : { "request" : { "referer" : CGI.HTTP_REFERER } }, + "http" : { "request" : { "referer" : CGI.HTTP_REFERER } }, "host" : { "name" : CGI.HTTP_HOST, "hostname" : CGI.SERVER_NAME }, "client" : { "ip" : CGI.REMOTE_ADDR }, "user" : {}, "user_agent" : { "original" : CGI.HTTP_USER_AGENT } }; - } + } -} \ No newline at end of file +} diff --git a/models/logging/LogstashAppender.cfc b/models/logging/LogstashAppender.cfc index ccac610..6dd5910 100644 --- a/models/logging/LogstashAppender.cfc +++ b/models/logging/LogstashAppender.cfc @@ -203,7 +203,7 @@ component "category" : loggerCat }, "message" : message, - "labels" : getProperty( "labels" ), + "labels" : getProperty( "labels" ), "event" : { "created" : dateTimeFormat( loge.getTimestamp(), "yyyy-mm-dd'T'HH:nn:ssZZ" ), "severity" : loge.getSeverity(), @@ -249,11 +249,8 @@ component "roles", "username" ]; - if( logObj.user.info.keyExists( "labels" ) && isStruct( logObj.user.info.labels ) ){ - logObj.labels.append( - logObj.user.info.labels, - true - ); + if ( logObj.user.info.keyExists( "labels" ) && isStruct( logObj.user.info.labels ) ) { + logObj.labels.append( logObj.user.info.labels, true ); logObj.user.info.delete( "labels" ); } userKeys.each( function( key ){ @@ -273,7 +270,7 @@ component } } - if( !structIsEmpty( getProperty( "labels" ) ) ){ + if ( !structIsEmpty( getProperty( "labels" ) ) ) { logObj.labels.append( getProperty( "labels" ), true ); } @@ -310,7 +307,10 @@ component logObj.package[ "reference" ] = event.getHTMLBaseURL(); if ( !logObj.labels.keyExists( "environment" ) ) { - logObj.labels[ "environment" ] = application.cbController.getSetting( name = "environment", defaultValue = "production" ); + logObj.labels[ "environment" ] = application.cbController.getSetting( + name = "environment", + defaultValue = "production" + ); } } diff --git a/models/util/Util.cfc b/models/util/Util.cfc index c2c762f..de3c62b 100644 --- a/models/util/Util.cfc +++ b/models/util/Util.cfc @@ -109,9 +109,9 @@ component accessors="true" singleton { ); } var message = "Your request was invalid. The response returned was #toJSON( errorPayload )#"; - var type = "cbElasticsearch.invalidRequest"; + var type = "cbElasticsearch.invalidRequest"; if ( len( errorReason ) && !isSimpleValue( errorPayload.error ) && errorPayload.error.keyExists( "type" ) ) { - type = "cbElasticsearch.native.#errorPayload.error.type#"; + type = "cbElasticsearch.native.#errorPayload.error.type#"; message = "An error was returned when communicating with the Elasticsearch server. The error received was: #errorReason#"; } else if ( isSimpleValue( errorPayload ) && !isJSON( errorPayload ) ) { message = "Elasticsearch server responded with [#response.getStatus()#]. The response received was not JSON.";