This is an automated email from the ASF dual-hosted git repository. chetanm pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk.git
The following commit(s) were added to refs/heads/master by this push: new 27a624e Improve splunk query (#4047) 27a624e is described below commit 27a624ecc763bc01eaa997ba9ce5fc55ad748221 Author: tysonnorris <tysonnor...@gmail.com> AuthorDate: Thu Oct 4 00:54:49 2018 -0700 Improve splunk query (#4047) Improves the Splunk query and log format * Log is now formatted similar to DockerToActivationLogStore * Query time offset is made configurable to account for log collection delays --- common/scala/src/main/resources/application.conf | 18 ++++++++++++++++++ .../core/containerpool/logging/SplunkLogStore.scala | 18 ++++++++++++++---- .../containerpool/logging/SplunkLogStoreTests.scala | 20 ++++++++++++++------ 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/common/scala/src/main/resources/application.conf b/common/scala/src/main/resources/application.conf index b2bcbc6..712b3ef 100644 --- a/common/scala/src/main/resources/application.conf +++ b/common/scala/src/main/resources/application.conf @@ -209,6 +209,24 @@ whisk { teardown-on-exit = true //set to true to disable the mesos framework on system exit; set for false for HA deployments } + logstore { + #SplunkLogStore configuration + #splunk { + # host = "splunkhost" #splunk api hostname + # port = 8089 #splunk api port + # username = "splunkapiusername" #splunk api username + # password = "splunkapipassword" #splunk api password + # index = "splunkindex" #splunk index name + # log-timestamp-field = "log_timestamp" #splunk field where timestamp is stored (to reflect log event generated time, not splunk's _time) + # log-stream-field = "log_stream" #splunk field where stream is stored (stdout/stderr) + # log-message-field = "log_message" #splunk field where log message is stored + # activation-id-field = "activation_id" #splunk field where activation id is stored + # query-constraints = "" #additional constraints for splunk queries + # query-timestamp-offset-seconds = "" #splunk query will be broadened by this 2*<offset value>; e.g. "earliest_time=activation.start - offset" and "latest_time=activation.end + offset" + # disableSNI = false #if true, disables hostname validation and cert validation (in case splunk api endpoint is using a self signed cert) + #} + } + # tracing configuration tracing { cache-expiry = 30 seconds #how long to keep spans in cache. Set to appropriate value to trace long running requests diff --git a/common/scala/src/main/scala/whisk/core/containerpool/logging/SplunkLogStore.scala b/common/scala/src/main/scala/whisk/core/containerpool/logging/SplunkLogStore.scala index 1cd93b5..f22ce33 100644 --- a/common/scala/src/main/scala/whisk/core/containerpool/logging/SplunkLogStore.scala +++ b/common/scala/src/main/scala/whisk/core/containerpool/logging/SplunkLogStore.scala @@ -60,8 +60,12 @@ case class SplunkLogStoreConfig(host: String, username: String, password: String, index: String, + logTimestampField: String, + logStreamField: String, logMessageField: String, activationIdField: String, + queryConstraints: String, + queryTimestampOffsetSeconds: Int, disableSNI: Boolean) case class SplunkResponse(results: Vector[JsObject]) object SplunkResponseJsonProtocol extends DefaultJsonProtocol { @@ -106,16 +110,18 @@ class SplunkLogStore( // {"preview":false,"init_offset":0,"messages":[],"fields":[{"name":"log_message"}],"results":[{"log_message":"some log message"}], "highlighted":{}} //note: splunk returns results in reverse-chronological order, therefore we include "| reverse" to cause results to arrive in chronological order val search = - s"""search index="${splunkConfig.index}"| spath ${splunkConfig.activationIdField}| search ${splunkConfig.activationIdField}=${activation.activationId.toString}| table ${splunkConfig.logMessageField}| reverse""" + s"""search index="${splunkConfig.index}"| spath ${splunkConfig.activationIdField}| search ${splunkConfig.queryConstraints} ${splunkConfig.activationIdField}=${activation.activationId.toString}| table ${splunkConfig.logTimestampField}, ${splunkConfig.logStreamField}, ${splunkConfig.logMessageField}| reverse""" val entity = FormData( Map( "exec_mode" -> "oneshot", "search" -> search, "output_mode" -> "json", - "earliest_time" -> activation.start.toString, //assume that activation start/end are UTC zone, and splunk events are the same + "earliest_time" -> activation.start + .minusSeconds(splunkConfig.queryTimestampOffsetSeconds) + .toString, //assume that activation start/end are UTC zone, and splunk events are the same "latest_time" -> activation.end - .plusSeconds(5) //add 5s to avoid a timerange of 0 on short-lived activations + .plusSeconds(splunkConfig.queryTimestampOffsetSeconds) //add 5s to avoid a timerange of 0 on short-lived activations .toString)).toEntity logging.debug(this, "sending request") @@ -130,7 +136,11 @@ class SplunkLogStore( .map(r => { ActivationLogs( r.results - .map(_.fields(splunkConfig.logMessageField).convertTo[String])) + .map(l => + //format same as whisk.core.containerpool.logging.LogLine.toFormattedString + f"${l.fields(splunkConfig.logTimestampField).convertTo[String]}%-30s ${l + .fields(splunkConfig.logStreamField) + .convertTo[String]}: ${l.fields(splunkConfig.logMessageField).convertTo[String].trim}")) }) }) } diff --git a/tests/src/test/scala/whisk/core/containerpool/logging/SplunkLogStoreTests.scala b/tests/src/test/scala/whisk/core/containerpool/logging/SplunkLogStoreTests.scala index dae47e6..c7d2935 100644 --- a/tests/src/test/scala/whisk/core/containerpool/logging/SplunkLogStoreTests.scala +++ b/tests/src/test/scala/whisk/core/containerpool/logging/SplunkLogStoreTests.scala @@ -60,15 +60,20 @@ class SplunkLogStoreTests "splunk-user", "splunk-pass", "splunk-index", + "log_timestamp", + "log_stream", "log_message", "activation_id", + "somefield::somevalue", + 22, disableSNI = false) behavior of "Splunk LogStore" val startTime = "2007-12-03T10:15:30Z" + val startTimePlusOffset = "2007-12-03T10:15:08Z" //queried end time range is endTime-22 val endTime = "2007-12-03T10:15:45Z" - val endTimePlus5 = "2007-12-03T10:15:50Z" //queried end time range is endTime+5 + val endTimePlusOffset = "2007-12-03T10:16:07Z" //queried end time range is endTime+22 val uuid = UUID() val user = Identity(Subject(), Namespace(EntityName("testSpace"), uuid), BasicAuthenticationAuthKey(uuid, Secret()), Set.empty) @@ -110,12 +115,12 @@ class SplunkLogStoreTests request.uri.path.toString() shouldBe "/services/search/jobs" request.headers shouldBe List(Authorization.basic(testConfig.username, testConfig.password)) - earliestTime shouldBe Some(startTime) - latestTime shouldBe Some(endTimePlus5) + earliestTime shouldBe Some(startTimePlusOffset) + latestTime shouldBe Some(endTimePlusOffset) outputMode shouldBe Some("json") execMode shouldBe Some("oneshot") search shouldBe Some( - s"""search index="${testConfig.index}"| spath ${testConfig.activationIdField}| search ${testConfig.activationIdField}=${activation.activationId.toString}| table ${testConfig.logMessageField}| reverse""") + s"""search index="${testConfig.index}"| spath ${testConfig.activationIdField}| search ${testConfig.queryConstraints} ${testConfig.activationIdField}=${activation.activationId.toString}| table ${testConfig.logTimestampField}, ${testConfig.logStreamField}, ${testConfig.logMessageField}| reverse""") ( Success( @@ -123,7 +128,7 @@ class SplunkLogStoreTests StatusCodes.OK, entity = HttpEntity( ContentTypes.`application/json`, - """{"preview":false,"init_offset":0,"messages":[],"fields":[{"name":"log_message"}],"results":[{"log_message":"some log message"},{"log_message":"some other log message"}], "highlighted":{}}"""))), + """{"preview":false,"init_offset":0,"messages":[],"fields":[{"name":"log_message"}],"results":[{"log_timestamp": "2007-12-03T10:15:30Z", "log_stream":"stdout", "log_message":"some log message"},{"log_timestamp": "2007-12-03T10:15:31Z", "log_stream":"stderr", "log_message":"some other log message"}], "highlighted":{}}"""))), userContext) } .recover { @@ -148,7 +153,10 @@ class SplunkLogStoreTests //use the a flow that asserts the request structure and provides a response in the expected format val splunkStore = new SplunkLogStore(system, Some(testFlow), testConfig) val result = await(splunkStore.fetchLogs(activation, context)) - result shouldBe ActivationLogs(Vector("some log message", "some other log message")) + result shouldBe ActivationLogs( + Vector( + "2007-12-03T10:15:30Z stdout: some log message", + "2007-12-03T10:15:31Z stderr: some other log message")) } it should "fail to connect to bogus host" in {