kong.log prefixes each line with the plugin name and request id, which is what makes a message findable. print and ngx.log lose that context.
kong.log.debug("resolved tenant ", tenant)
kong.log.info("cache miss for ", key)
kong.log.warn("upstream slow: ", latency, "ms")
kong.log.err("redis unreachable: ", err)
-- structured, for a log collector
kong.log.set_serialize_value("tenant", tenant)
# kong.conf
log_level = notice # debug | info | notice | warn | error | crit
2026/08/25 09:12:44 [info] 1234#0: *5821 [kong] handler.lua:42
[my-plugin] cache miss for tenant:acme, client: 10.0.1.4,
request: "GET /billing HTTP/1.1"
# kong.log.debug costs nothing when log_level is above debug - the message
# is not even concatenated. Prefer several arguments over building a string
# with .. so that stays true.
#
# set_serialize_value adds a field to the JSON record the logging plugins
# emit, which is how you get a custom field into http-log.
Run this yourself in the Online Java Compiler, spin up a live REST API in the API Sandbox, or practise with Java interview questions.
Published 2026-08-25