Skip to content

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