-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support time-series DB using Kamon Tag #3343
Conversation
If it’s optional and and is false by default I don’t see an issue to add in the grand scheme of things. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding this! 🎉
Got a lengthy question on the API design here, let's see what you think.
.counter(token.toString) | ||
.increment(1) | ||
if (token.tags.nonEmpty) metrics.counter(token.toString, token.tags.get).increment(1) | ||
else metrics.counter(token.toString).increment(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be:
token.tags match {
case Some(tags) => metrics.counter(token.toString, token.tags).increment(1)
case None => metrics.counter(token.toString).increment(1)
}
Same below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edit: Might be ignorable, see lengthy comment.
def INVOKER_CONTAINER_START(containerState: String) = | ||
LogMarkerToken(invoker, s"container_start_${containerState}", count) | ||
def INVOKER_KUBECTL_CMD(cmd: String) = LogMarkerToken(invoker, s"kubectl.$cmd", start) | ||
def INVOKER_DOCKER_CMD(cmd: String) = LogMarkerToken(invoker, "docker", start, Map("cmd" → cmd)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove all ASCII arrows and replace them with ->
(memo to self: automate this check)
} | ||
|
||
private object Emitter { | ||
val timeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZone(ZoneId.of("UTC")) | ||
} | ||
|
||
case class LogMarkerToken(component: String, action: String, state: String) { | ||
case class LogMarkerToken(component: String, action: String, state: String, tags: Option[Map[String, String]] = None) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been looking through all the PR, and it feels a bit unclear to me to grasp all the branches on when something will be logged/sent and how. Maybe we can simplify:
What do you think about creating
LogMarkerToken(component: String, action: String, state: String, macroTags: Map[String, String] = Map.empty, microTags: Map[String, String] = Map.empty)
Then we could have
override def toString = component + "_" + action + "_" + state
override def toStringWithTags = component + "_" + action + macroTags.mkString(".", ".", "") + "_" + state
And call them in the specific call-site accordingly:
def emitCounterMetric(token: LogMarkerToken): Unit = {
if (TransactionId.metricsKamon) {
if (TransactionId.metricsKamonTags) {
metrics.counter(token.toString, token.macroTags ++ token.microTags).increment(1)
} else {
metrics.counter(token.toStringWithTags).increment(1)
}
}
}
We maybe don't even need the granular option then. If the backend supports tags, we're fine anyway, right? We just need to make sure we only log the macro tags if the backend does not support "true" tags.
WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I agree with this all and think it is a very clear solution.
I will modify the code, test it, and then commit it again.
Thank you for your kind review!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@markusthoemmes
I changed the code, but I'm worried also the scaleable issue that occurs when using a low-capacity and low-performance Kamon-backend that supports tags.
Should we make a granular option again?
What do you think..?
} | ||
|
||
private object Emitter { | ||
val timeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZone(ZoneId.of("UTC")) | ||
} | ||
|
||
case class LogMarkerToken(component: String, action: String, state: String) { | ||
case class LogMarkerToken(component: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A ScalaDoc comment here would be great, stating that macroTags
should be used for tags with a bounded number of permutations only while microTags can be used for whatever granularity you might need.
val token = LogMarkerToken("http", s"${m.toLowerCase}.${res.status.intValue}", LoggingMarkers.count) | ||
val token = LogMarkerToken( | ||
"http", | ||
s"${m.toLowerCase}", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't need string interpolation anymore. Just m.toLowerCase
should suffice.
def INVOKER_CONTAINER_START(containerState: String, namespaceName: String, actionName: String) = | ||
LogMarkerToken( | ||
invoker, | ||
s"containerStart", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No string interpolation needed. Just containerStart
is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified. thank you
@markusthoemmes could you please review again? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some more nits. We shouldn't entangle the LogMarkerTokens with a global value.
@mhenke1 could you please review the change in INVOKER_CONTAINER_START
?
It changes from container_start_cold
to containerStart.cold
override def toString() = component + "_" + action + "_" + state | ||
|
||
// folderLeft is used instead of mkString(".", ".", ""), | ||
// because Map.empty.mkString returns "." | ||
def toStringWithTags = component + "_" + action + getTags.values.foldLeft("")((a, v) => a + "." + v) + "_" + state |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can simplify to
def toStringWithTags(includeMicroTags: Boolean) = {
val tags = if(includeMicroTags) macroTags ++ microTags else macroTags
val tagString = tags.values.foldLeft("")(_ + "." + _) // graceful handling of an empty map
component + "_" + action + tagString + "_" + state
}
Note: I'd remove the entanglement of this instance to some arbitrary global value and pass it in instead for better control (in tests for instance).
if (TransactionId.metricsKamonTags) { | ||
metrics.counter(token.toString, token.getTags).increment(1) | ||
} else { | ||
metrics.counter(token.toStringWithTags).increment(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes to
metrics.counter(token.toStringWithTags(includeMicroTags = false)).increment(1)
.counter(token.toString) | ||
.increment(1) | ||
if (TransactionId.metricsKamonTags) { | ||
metrics.counter(token.toString, token.getTags).increment(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes to
metrics.counter(token.toString, if(TransactionId.granularMetric) token.allTags else token.macroTags).increment(1)
// folderLeft is used instead of mkString(".", ".", ""), | ||
// because Map.empty.mkString returns "." | ||
def toStringWithTags = component + "_" + action + getTags.values.foldLeft("")((a, v) => a + "." + v) + "_" + state | ||
def getTags = if (TransactionId.granularMetric) macroTags ++ microTags else macroTags |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about def allTags = macroTags ++ microTags
and use it from the outside?
* @param component Component like invoker, controller, and docker. It is defined in LoggingMarkers. | ||
* @param action Action of the component. | ||
* @param state State of the action. | ||
* @param macroTags macroTags should be used for tags with a bounded number of permutations. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add an example for such a bounded tag (e.g. http staus code)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm (please see one comment regarding parameter explaination)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@markusthoemmes has it been resolved? |
@upgle it's merged yep, you can now rebase and try if the test passes (i think it won't). |
The test is passed, and some test code has modified to use log parser which is changed the signature. |
conflicts have been resolved. |
PG3 1939 🔵 |
This PR is for supporting time-series database using Kamon Tag like OpenTSDB, Datadog.
and I also added granular metric again which was removed from #3284 PR. (but it is optional, default is False) because we need this feature for tracking activities per namespace, and already have built up enough infrastructure.
Reference
http://opentsdb.net/docs/build/html/user_guide/writing.html
https://docs.datadoghq.com/agent/tagging/
Metrics tested in real environment.
* It is tested on both OpenTSDB and Graphite
1. OpenTSDB (Tag based aggregation)
In OpenTSDB, you can use regexable aggregation query with tag only.
e.g.
host=regexp(web[0-9]+.lax.mysite.com)
2. Graphite (Metric name based aggregation)
In Graphite, you can use regexable aggregation query with a metric name only.
e.g.
invoker_containerStart_*