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 {

Reply via email to