-
Notifications
You must be signed in to change notification settings - Fork 319
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 ECS-reformatting OVERRIDE #1793
Conversation
💚 Build Succeeded
Expand to view the summary
Build stats
Test stats 🧪
Trends 🧪💚 Flaky test reportTests succeeded. Expand to view the summary
Test stats 🧪
|
...m-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2AppenderGetLayoutAdvice.java
Show resolved
Hide resolved
...er-plugin-common/src/main/java/co/elastic/apm/agent/log/shader/AbstractLogShadingHelper.java
Outdated
Show resolved
Hide resolved
...ugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2LogShadingHelper.java
Outdated
Show resolved
Hide resolved
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. Just some minor stylistic comments.
* Used when {@link LoggingConfiguration#logEcsReformatting log_ecs_reformatting} is set to | ||
* {@link LogEcsReformatting#SHADE SHADE} or {@link LogEcsReformatting#REPLACE REPLACE}. | ||
*/ | ||
private static final WeakConcurrentMap<Object, Object> originalAppender2ecsAppender = WeakMapSupplier.createMap(); |
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.
Why are all maps static?
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 habit 🙂
This guarantees there are no duplication, no matter where and how many helpers are created. Conceptually, everything here could be static, but with the maps this may actually have an effect if not.
Any reason to make them non-static? Are you worried from higher contention or something?
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.
If they weren't static, we could use generics WeakConcurrentMap<A, A>
/WeakConcurrentMap<A, R>
which could make things a bit more readable. As we already have a static singleton instance, I was just a bit confused why the maps need to be shared across the different implementations.
That's not a blocker though.
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's a good point, but since I use dummy formatter and appender to optimize lookups that can't or shouldn't result with real implementations, switching to generic maps means adding the complication of creating these dummies in the helper subclasses. I like the fact that it is hidden from the sub-helpers, but I can try this out
* Therefore, by instrumenting this method and replacing the returned {@link Layout}, we can implement the | ||
* {@link co.elastic.apm.agent.logging.LogEcsReformatting#OVERRIDE OVERRIDE} use case. | ||
*/ | ||
public class Log4j2AppenderGetLayoutAdvice { |
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 think we should agree on where to put advices. Either as static inner classes or as stand-alone classes.
I'd prefer the static inner class approach a lot. It makes instrumentations more cohesive as it's easier to grasp where an advice is applied to.
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 agree it's much more readable this way, the thing is that it's much easier to get it wrong and make the instrumentation dependent on library types. I fixed quite a few of those lately, and I think a main reason for that is allowing something in the import
section, that is not allowed somewhere in the class.
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.
Are you thinking about automatically validating the imports? Or are you saying that it's just easier for the reviewer to verify that there are no invalid imports?
I just had an idea how we could verify that the instrumentation doesn't use invalid classes. In AbstractInstrumentationTest
, we could create an isolated child class loader (parent=bootstrap) that loads each instrumentation class and calls the get*Matcher
methods. That would result in linkage errors if library types are referenced.
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.
Are you thinking about automatically validating the imports? Or are you saying that it's just easier for the reviewer to verify that there are no invalid imports?
Auto-validating the imports means looking at source, so probably not. My experience shows that it's easier for the author to add a reference to something that is already at the imports in the IDE, and easier for the reviewer to miss.
I just had an idea how we could verify that the instrumentation doesn't use invalid classes. In AbstractInstrumentationTest, we could create an isolated child class loader (parent=bootstrap) that loads each instrumentation class and calls the get*Matcher methods. That would result in linkage errors if library types are referenced.
Good idea! You mean a separate URL CL that knows the agent jar and is the child of the boot CL (since it's a unit test, we cannot rely on the boot CL to load agent classes)?
The only thing is that we will still rely only on tested JVMs, and may be exposed to differences in linkage in non tested ones, but should capture such issues very quickly in most cases.
// Effectively disables instrumentation to all database appenders | ||
return null; | ||
} | ||
Layout<? extends Serializable> ecsLayout = Log4J2EcsReformattingHelper.instance().getEcsOverridingFormatterFor(thisAppender); |
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.
Add a static field for the helper.
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.
(also applies to other similar places)
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.
Sure
return thisAppender instanceof FileAppender && eventObject instanceof ILoggingEvent && | ||
LogbackLogShadingHelper.instance().shouldSkipAppend((FileAppender<ILoggingEvent>) thisAppender); | ||
|
||
return eventObject instanceof ILoggingEvent && LogbackEcsReformattingHelper.instance().onAppendEnter(thisAppender); |
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's a pitty that we can't use @Local
in indy advices. Propagating the config value from the enter to the exit advice that way would be neat. But ofc. the ThreadLocal also works 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.
It wouldn't be enough in this case though, because in log4j2 we need to use it in a separate advice as well
|
||
import javax.annotation.Nullable; | ||
|
||
class LogbackEcsReformattingHelper extends AbstractEcsReformattingHelper<OutputStreamAppender<ILoggingEvent>, Encoder<ILoggingEvent>> { |
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 really like how straightforward the implementations now are.
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!
The abstract helper contains some complexity, but not as bad as I feared and I am also satisfied with how library helpers ended up being all about library-specific-ECS-logging and the advices are minimal and almost identical.
Closes #1617 |
What does this PR do?
Adds the ability to
OVERRIDE
original logs with ECS-formatted events. As opposed toSHADE
andREPLACE
options, this is not limited only to log files, but to additional logging options. Most importantly, it allows to reformat System.out logs to ECS, thus enabling native Docker/k8s support.Mechanism specifications:
log_ecs_reformatting
configecs.json
file where not requiredLayout
in log4j orEncoder
in Logback)Checklist