Skip to content
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 Graylog Extended Log Format (GELF) for structured logging #42158

Conversation

slissner
Copy link
Contributor

@slissner slissner commented Sep 5, 2024

Summary

As of Spring Boot v3.4, structured logging is now built-in into Spring Boot.

The following pull requests adds a Graylog Extended Log Format (GELF) formatter to the structured logging of Spring Boot.

Features

  • Add gelf structured logging formatter with Logback
  • Add gelf structured logging formatter with Log4j2
  • Describe GELF Formatter in the logging documentation
  • Add logging.structured.gelf.service.* properties to additional-spring-configuration-metadata.json

Examples

Log messages

Example INFO log in GELF format:

{"version":"1.1","short_message":"Hello structured logging!","timestamp":1.725547337613E9,"level":6,"_level_name":"INFO","_process_pid":16597,"_process_thread_name":"main","host":"spring-boot-gelf","_log_logger":"com.slissner.springbootgelf.ExampleLogger","_testkey_testmessage":"test","_userId":"1"}

Example ERROR log with stack trace in GELF format:

{"version":"1.1","short_message":"Test exception","timestamp":1.725547337719E9,"level":3,"_level_name":"ERROR","_process_pid":16597,"_process_thread_name":"main","host":"spring-boot-gelf","_log_logger":"com.slissner.springbootgelf.ExampleLogger","full_message":"Test exception\n\njava.lang.RuntimeException: Boom\n\tat com.slissner.springbootgelf.ExampleLogger.run(ExampleLogger.java:23) ~[main\/:?]\n\tat org.springframework.boot.SpringApplication.lambda$callRunner$5(SpringApplication.java:792) ~[spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat org.springframework.util.function.ThrowingConsumer$1.acceptWithException(ThrowingConsumer.java:83) ~[spring-core-6.2.0-M7.jar:6.2.0-M7]\n\tat org.springframework.util.function.ThrowingConsumer.accept(ThrowingConsumer.java:60) ~[spring-core-6.2.0-M7.jar:6.2.0-M7]\n\tat org.springframework.util.function.ThrowingConsumer$1.accept(ThrowingConsumer.java:88) ~[spring-core-6.2.0-M7.jar:6.2.0-M7]\n\tat org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:800) ~[spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:791) ~[spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat org.springframework.boot.SpringApplication.lambda$callRunners$3(SpringApplication.java:776) ~[spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat java.base\/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) [?:?]\n\tat java.base\/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357) [?:?]\n\tat java.base\/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510) [?:?]\n\tat java.base\/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) [?:?]\n\tat java.base\/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) [?:?]\n\tat java.base\/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) [?:?]\n\tat java.base\/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) [?:?]\n\tat java.base\/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) [?:?]\n\tat org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:776) [spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat org.springframework.boot.SpringApplication.run(SpringApplication.java:326) [spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat org.springframework.boot.SpringApplication.run(SpringApplication.java:1365) [spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) [spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat com.slissner.springbootgelf.SpringBootGelfApplication.main(SpringBootGelfApplication.java:10) [main\/:?]\n","_error_type":"java.lang.RuntimeException","_error_stack_trace":"java.lang.RuntimeException: Boom\n\tat com.slissner.springbootgelf.ExampleLogger.run(ExampleLogger.java:23) ~[main\/:?]\n\tat org.springframework.boot.SpringApplication.lambda$callRunner$5(SpringApplication.java:792) ~[spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat org.springframework.util.function.ThrowingConsumer$1.acceptWithException(ThrowingConsumer.java:83) ~[spring-core-6.2.0-M7.jar:6.2.0-M7]\n\tat org.springframework.util.function.ThrowingConsumer.accept(ThrowingConsumer.java:60) ~[spring-core-6.2.0-M7.jar:6.2.0-M7]\n\tat org.springframework.util.function.ThrowingConsumer$1.accept(ThrowingConsumer.java:88) ~[spring-core-6.2.0-M7.jar:6.2.0-M7]\n\tat org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:800) ~[spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:791) ~[spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat org.springframework.boot.SpringApplication.lambda$callRunners$3(SpringApplication.java:776) ~[spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat java.base\/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) [?:?]\n\tat java.base\/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357) [?:?]\n\tat java.base\/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510) [?:?]\n\tat java.base\/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) [?:?]\n\tat java.base\/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) [?:?]\n\tat java.base\/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) [?:?]\n\tat java.base\/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) [?:?]\n\tat java.base\/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) [?:?]\n\tat org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:776) [spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat org.springframework.boot.SpringApplication.run(SpringApplication.java:326) [spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat org.springframework.boot.SpringApplication.run(SpringApplication.java:1365) [spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) [spring-boot-3.4.0-SNAPSHOT.jar:3.4.0-SNAPSHOT]\n\tat com.slissner.springbootgelf.SpringBootGelfApplication.main(SpringBootGelfApplication.java:10) [main\/:?]\n","_error_message":"Boom"}

Screenshots

Search Dashboard

Search Dashboard

INFO log:

INFO log

ERROR log:

ERROR log

Local Testing

In order to test this pull request locally, please have a look at the following demo applications:

  • A demo application for Logback
  • Another demo application for Log4j2

Only the demo application for Logback is set up to actually send logs to a local Graylog instance. It uses an UDP log appender from the https://github.com/osiegmar/logback-gelf library.

Please see the above mentioned repository for further explanation on how to set up quickly a local instance of Graylog.

See also

- Add `gelf` structured logging formatter with Logback
- Add `gelf` structured logging formatter with Log4j2
- Describe GELF Formatter in the logging documentation
- Add `logging.structured.gelf.service.*` properties to `additional-spring-configuration-metadata.json`
@pivotal-cla
Copy link

@slissner Please sign the Contributor License Agreement!

Click here to manually synchronize the status of this Pull Request.

See the FAQ for frequently asked questions.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Sep 5, 2024
@pivotal-cla
Copy link

@slissner Thank you for signing the Contributor License Agreement!

Copy link
Contributor

@mhalbritter mhalbritter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! I've left a few comments for your consideration.

@mhalbritter mhalbritter added the status: waiting-for-feedback We need additional information before we can continue label Sep 9, 2024
@mhalbritter mhalbritter self-assigned this Sep 10, 2024
@mhalbritter mhalbritter added this to the 3.4.x milestone Sep 10, 2024
@mhalbritter mhalbritter added type: enhancement A general enhancement and removed status: waiting-for-feedback We need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged labels Sep 10, 2024
@mhalbritter
Copy link
Contributor

Thank you very much and congratulations on your first contribution 🎉! I've polished a bit in b5e7302, for example fixing the double timestamp formatting.

@mhalbritter mhalbritter modified the milestones: 3.4.x, 3.4.0-M3 Sep 10, 2024
@slissner
Copy link
Contributor Author

Thank you very much @mhalbritter for finalizing this!! I wanted to work on your issues this Friday, but you were faster :) Awesome to see GELF integrated with Spring Boot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants