-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Add %{{TIME_NOW}} pattern for sprintf #16906
Conversation
📃 DOCS PREVIEW ✨ https://logstash_bk_16906.docs-preview.app.elstc.co/diff |
📃 DOCS PREVIEW ✨ https://logstash_bk_16906.docs-preview.app.elstc.co/diff |
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.
Left a couple of observations.
@@ -149,6 +151,24 @@ public void TestValueIsHash() throws IOException { | |||
assertEquals("{\"k1\":\"v\"}", StringInterpolation.evaluate(event, path)); | |||
} | |||
|
|||
@Test | |||
public void TestTimeNow() throws IOException { |
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 know that the other test methods starts with capital letter, but in Java methods starts with lowercase.
I would also suggest to rename:
TestTimeNow
->testGivenAnEventWithTimeNowSubstitutionVariableWhenEvaluatedReplaceWithCurrentTimestamp
TestBadTimeNow
->testGivenAnEventWithASubstitutionVariableThatContainsTheTimeNowMarkerWhenEvaluatedNoReplacementWithCurrentTimestampHappen
// A special pattern to generate a fresh current time | ||
// - `%{{TIME_NOW}}` -> `2025-01-16T16:57:12.488955Z` | ||
close = close + 1; // consume extra closing squiggle | ||
final Timestamp t = event.getTimestamp(); | ||
if (t != null) { | ||
final String javaTimeFormatPattern = template.substring(open+3, close-1); | ||
final java.time.format.DateTimeFormatter javaDateTimeFormatter = DateTimeFormatter.ofPattern(javaTimeFormatPattern).withZone(ZoneOffset.UTC); | ||
final String formattedTimestamp = javaDateTimeFormatter.format(t.toInstant()); | ||
builder.append(formattedTimestamp); | ||
final String pattern = template.substring(open+3, close-1); | ||
if (pattern.equals(TIME_NOW)) { | ||
final Timestamp now = new Timestamp(); | ||
builder.append(now); | ||
} else { | ||
final Timestamp t = event.getTimestamp(); | ||
if (t != null) { | ||
final DateTimeFormatter javaDateTimeFormatter = DateTimeFormatter.ofPattern(pattern).withZone(ZoneOffset.UTC); | ||
final String formattedTimestamp = javaDateTimeFormatter.format(t.toInstant()); | ||
builder.append(formattedTimestamp); | ||
} |
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.
Maybe all this time related logic could be extracted in separate helper method.
Timestamp before = new Timestamp(); | ||
Timestamp result = new Timestamp(StringInterpolation.evaluate(event, pattern)); | ||
Timestamp after = new Timestamp(); |
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.
Secondary note, that you could leave out: I know that it's hard two creation of Instant
falls into the same nanosecond, but without a minimal sleep we can't grant that the Timestamps a monotonically increasing, they could result in same value.
📃 DOCS PREVIEW ✨ https://logstash_bk_16906.docs-preview.app.elstc.co/diff |
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.
Once fixed the issue with SonarCube and we get a 🟢 LGTM
public void testPatternTimeNowGenerateFreshTimestamp() throws IOException, InterruptedException { | ||
Event event = getTestEvent(); | ||
Timestamp before = new Timestamp(); | ||
TimeUnit.NANOSECONDS.sleep(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.
It seems SonarCube is not happy with this
TimeUnit.NANOSECONDS.sleep(1);
Quality Gate passedIssues Measures |
💚 Build Succeeded
History
|
📃 DOCS PREVIEW ✨ https://logstash_bk_16906.docs-preview.app.elstc.co/diff |
@logstashmachine backport 8.x |
* Add a new pattern %{{TIME_NOW}} to `event.sprintf` to generate a fresh timestamp. The timestamp is represented as a string in the default ISO 8601 format For example, ``` input { heartbeat { add_field => { "heartbeat_time" => "%{{TIME_NOW}}" } } } ``` (cherry picked from commit cd729b7)
* Add a new pattern %{{TIME_NOW}} to `event.sprintf` to generate a fresh timestamp. The timestamp is represented as a string in the default ISO 8601 format For example, ``` input { heartbeat { add_field => { "heartbeat_time" => "%{{TIME_NOW}}" } } } ``` (cherry picked from commit cd729b7) Co-authored-by: kaisecheng <[email protected]>
Release notes
Add support of
%{{TIME_NOW}}
syntax to sprinf format to generate a new current timestampWhat does this PR do?
This commit added
%{{TIME_NOW}}
syntax toevent.sprintf()
to generate a fresh timestamp as stringExample
It gives two different timestamps.
Why is it important/What is the impact to the user?
When the incoming event has value in
@timestamp
, eg. event from agent, Logstash use the provided value instead of generating a new timestamp. Prior to the change, there is no way to force a new timestamp to generate during the execution of input plugins.Previously, users often generate timestamp with ruby-filter
code => "event.set('ruby_time', Time.now());"
, but this approach requires events to pass through PQ before the timestamp was assigned. It is not a good indicator of when the event actually arrived in Logstash.With this change, all input and filter plugins can generate new timestamps
Checklist
Author's Checklist
How to test this PR locally
Related issues
Use cases
Screenshots
Logs