In my Grails 4 app, log.info("log message") doesn't show log, but log.error("log message") does.
How do I change the log level from error to info in Grails 4?
Option 1
All I needed to do was update the application.yml file and added the following to the bottom
logging:
level:
root: INFO
You can also set a single the log level for a single package:
logging:
level:
packageName: INFO
Option 2
Since Grails 4 is based on Spring Boot, I ended up just setting the appropriate environment variable, i.e. logging.level.root=INFO or logging.level.com.mycompany.mypackage=INFO which I did in intellij by editing my run configuration (see below). This way, I can set the logging level differently when I deploy it.
You want to edit grails-app/conf/logback.groovy. Below is what the default file looks like for Grails 4.0.1.
import grails.util.BuildSettings
import grails.util.Environment
import org.springframework.boot.logging.logback.ColorConverter
import org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter
import java.nio.charset.StandardCharsets
conversionRule 'clr', ColorConverter
conversionRule 'wex', WhitespaceThrowableProxyConverter
// See http://logback.qos.ch/manual/groovy.html for details on configuration
appender('STDOUT', ConsoleAppender) {
encoder(PatternLayoutEncoder) {
charset = StandardCharsets.UTF_8
pattern =
'%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} ' + // Date
'%clr(%5p) ' + // Log level
'%clr(---){faint} %clr([%15.15t]){faint} ' + // Thread
'%clr(%-40.40logger{39}){cyan} %clr(:){faint} ' + // Logger
'%m%n%wex' // Message
}
}
def targetDir = BuildSettings.TARGET_DIR
if (Environment.isDevelopmentMode() && targetDir != null) {
appender("FULL_STACKTRACE", FileAppender) {
file = "${targetDir}/stacktrace.log"
append = true
encoder(PatternLayoutEncoder) {
charset = StandardCharsets.UTF_8
pattern = "%level %logger - %msg%n"
}
}
logger("StackTrace", ERROR, ['FULL_STACKTRACE'], false)
}
root(ERROR, ['STDOUT'])
The specific change depends on what you really want to do. For example, if you have a controller named demo.SomeController and you want to set its log level to INFO, you could add something like this:
logger 'demo.SomeController', INFO, ['STDOUT'], false
See http://logback.qos.ch/manual/groovy.html for the full config reference.
I hope that helps.
Simple Way:
Update/Replace your grails-app/conf/logback.groovy with following code:
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
appender("FILE", RollingFileAppender) {
file = "logs/FILE-NAME.log"
rollingPolicy(TimeBasedRollingPolicy) {
fileNamePattern = "logs/FILE-NAME-%d{yyyy-MM-dd}.log"
maxHistory = 30
}
encoder(PatternLayoutEncoder) {
pattern = "%d{HH:mm:ss.SSS} %-4relative [%thread] %-5level %logger{35} - %msg%n"
}
}
root(INFO, ["FILE"])
Above solution shows logger level to INFO
You can refer this more details and all log levels.
Hope this will helps you.
Related
I have a log filter that logs out essential request information for debugging and log analytics. But as you can see, the text payload is really hard to read.
I don't want to have to copy + paste this text payload into a text editor every single time. Is there a way to make stack driver print this in a collapsable json instead?
More info:
- GKE pod
#Component
class LogFilter : WebFilter {
private val logger = LoggerFactory.getLogger(LogFilter::class.java)
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
return chain
.filter(exchange)
.doAfterTerminate {
val request = exchange.request
val path = request.uri.path
val routesToExclude = listOf("actuator")
var isExcludedRoute = false
for (r in routesToExclude) { if (path.contains(r)) { isExcludedRoute = true; break; } }
if (!isExcludedRoute) {
val startTime = System.currentTimeMillis()
val statusCode = exchange.response.statusCode?.value()
val requestTime = System.currentTimeMillis() - startTime
val msg = "Served $path as $statusCode in $requestTime msec"
val requestPrintMap = mutableMapOf<Any, Any>()
requestPrintMap["method"] = if (request.method != null) {
request.method.toString()
} else "UNKNOWN"
requestPrintMap["path"] = path.toString()
requestPrintMap["query_params"] = request.queryParams
requestPrintMap["headers"] = request.headers
requestPrintMap["status_code"] = statusCode.toString()
requestPrintMap["request_time"] = requestTime
requestPrintMap["msg"] = msg
logger.info(JSONObject(requestPrintMap).toString())
}
}
}
}
What you will need to do is customize Fluentd in GKE. Pretty much it's creating a Fluend daemonset for logging instead of the default logging method.
Once that is done, you can setup structured logging to send jsonPayload logs to Stackdriver Logging.
The default Stackdriver logging agent configuration for Kubernetes will detect single-line JSON and convert it to jsonPayload. You can configure Spring to log as single-line JSON (e.g., via JsonLayout1) and let the logging agent pick up the JSON object (see https://cloud.google.com/logging/docs/agent/configuration#process-payload).
1Some of the JSON field names are different (e.g., JsonLayout uses "level" for the log level, while the Stackdriver logging agent recognizes "severity"), so you may have to override addCustomDataToJsonMap to fully control the resulting log entries.
See also GKE & Stackdriver: Java logback logging format?
I am trying to do MDC propagation with Kamon like shown in this documentation But it does not seem to work like they say
Play framework - 2.5
kamon-core - 0.6.2
kamon-play-25 - 0.6.2
My logback pattern:
<pattern>%d{HH:mm:ss.SSS} [%thread] [%level] [%traceToken]- %logger{36}\(%L\) %X{X-ApplicationId} - %message%n%xException</pattern>
I have created a filter:
class AccessLoggingFilter #Inject() (implicit val mat: Materializer, ec: ExecutionContext) extends Filter with LazyLogging {
val ApplicationIdKey = AvailableToMdc("X-ApplicationId")
def apply(next: (RequestHeader) => Future[Result])(request: RequestHeader): Future[Result] = {
TraceLocal.storeForMdc("X-ApplicationId", request.id.toString)
logger.error("first Location")
withMdc {
logger.error("Second location")
next(request)
}}}
And added it like so:
class MyFilters #Inject() (accessLoggingFilter: AccessLoggingFilter) extends DefaultHttpFilters(accessLoggingFilter)
Now when i do an http call to the server i get the following output:
c.v.i.utils.AccessLoggingFilter(24) - first Location
c.v.i.utils.AccessLoggingFilter(26) 1 - Second location
And all log prints afterwards do not show the '1' X-ApplicationId
Cant figure out what i am doing wrong.
Here a complete(almost) example:
build.sbt:
name := "kamon-play-example"
version := "1.0"
scalaVersion := "2.11.7"
val kamonVersion = "0.6.2"
val resolutionRepos = Seq("Kamon Repository Snapshots" at "http://snapshots.kamon.io")
val dependencies = Seq(
"io.kamon" %% "kamon-play-25" % kamonVersion,
"io.kamon" %% "kamon-log-reporter" % kamonVersion
)
lazy val root = (project in file(".")).enablePlugins(PlayScala)
.settings(resolvers ++= resolutionRepos)
.settings(libraryDependencies ++= dependencies)
basic filter:
class TraceLocalFilter #Inject() (implicit val mat: Materializer, ec: ExecutionContext) extends Filter {
val logger = Logger(this.getClass)
val TraceLocalStorageKey = "MyTraceLocalStorageKey"
val userAgentHeader = "User-Agent"
//this value will be available in the MDC at the moment to call to Logger.*()s
val UserAgentHeaderAvailableToMDC = AvailableToMdc(userAgentHeader)
override def apply(next: (RequestHeader) ⇒ Future[Result])(header: RequestHeader): Future[Result] = {
def onResult(result:Result) = {
val traceLocalContainer = TraceLocal.retrieve(TraceLocalKey).getOrElse(TraceLocalContainer("unknown","unknown"))
result.withHeaders(TraceLocalStorageKey -> traceLocalContainer.traceToken)
}
//update the TraceLocalStorage
TraceLocal.store(TraceLocalKey)(TraceLocalContainer(header.headers.get(TraceLocalStorageKey).getOrElse("unknown"), "unknown"))
TraceLocal.store(UserAgentHeaderAvailableToMDC)(header.headers.get(userAgentHeader).getOrElse("unknown"))
//call the action
next(header).map(onResult)
}
}
we need add the filter:
class Filters #Inject() (traceLocalFilter: TraceLocalFilter) extends HttpFilters {
val filters = Seq(traceLocalFilter)
}
a really simple controller and action:
class KamonPlayExample #Inject() (kamon: Kamon) extends Controller {
def sayHello = Action.async {
Future {
logger.info("Say hello to Kamon")
Ok("Say hello to Kamon")
}
}
}
in logback.xml add the following pattern:
<pattern>%date{HH:mm:ss.SSS} %-5level [%traceToken][%X{User-Agent}] [%thread] %logger{55} - %msg%n</pattern>
add the sbt-aspectj-runner plugin in order to run the application with Aspectjweaver in DEV mode:
addSbtPlugin("io.kamon" % "aspectj-play-runner" % "0.1.3")
run the application with aspectj-runner:run and make some curls:
curl -i -H 'X-Trace-Token:kamon-test' -H 'User-Agent:Super-User-Agent' -X GET "http://localhost:9000/helloKamon"
curl -i -H 'X-Trace-Token:kamon-test'-X GET "http://localhost:9000/helloKamon"
in the console:
15:09:16.027 INFO [kamon-test][Super-User-Agent] [application-akka.actor.default-dispatcher-8] controllers.KamonPlayExample - Say hello to Kamon
15:09:24.034 INFO [kamon-test][curl/7.47.1] [application-akka.actor.default-dispatcher-8] controllers.KamonPlayExample - Say hello to Kamon
hope you help.
I have a Spring Boot app that (basically) has the following project structure:
myapp/
src/
<All Java source code here>
build.gradle
application.yml
logback.groovy
And its build.gradle dependencies are:
dependencies {
compile(
'org.springframework.boot:spring-boot-starter-actuator'
,'org.springframework.boot:spring-boot-starter-jetty'
,'org.springframework.boot:spring-boot-starter-security'
,'org.springframework.boot:spring-boot-starter-thymeleaf'
,'org.apache.commons:commons-lang3:3.4'
,'ch.qos.logback:logback-parent:1.1.7'
)
compile('org.springframework.boot:spring-boot-starter-web') {
exclude module: 'spring-boot-starter-tomcat'
}
}
The application.yml:
logging:
level:
org.springframework.web: 'DEBUG'
server:
error:
whitelabel:
enabled: false
spring:
datasource:
test-on-borrow: true
validation-query: SELECT 1
messages:
basename: i18n/messages
And the logback.groovy:
statusListener(OnConsoleStatusListener)
appender('CONSOLE', ConsoleAppender) {
encoder(PatternLayoutEncoder) {
pattern = '%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n'
}
}
appender('ROLLING', RollingFileAppender) {
encoder(PatternLayoutEncoder) {
Pattern = '%d %level %thread %mdc %logger - %m%n'
}
rollingPolicy(TimeBasedRollingPolicy) {
FileNamePattern = '/Users/myuser/logs/myapp/myapp-%d{yyyy-MM}.zip'
}
}
When I run ./gradlew bootRun -Pspring.config=. (where spring.config=. implies the myapp/application.yml config file), I see console output only. Nothing gets logged to /Users/myuser/logs/myapp/. Any ideas as to why?
Update
I have created a barebones Spring Boot app that uses my identical Logback config:
https://github.com/hotmeatballsoup/spring-boot-logback-example
Clone it and run it by running:
./gradlew bootRun -Pspring.config=.
Note that even though the app starts up fine (and you will see lots of console output), that it does not in fact create a /var/log/spring-boot-logback-example/spring-boot-logback-example.log file as expected!
add to following to logback.groovy
root(DEBUG, ["CONSOLE", "ROLLING"])
I am able to run the application successfully after making the following changes to your code:
Moved line root(DEBUG, ["CONSOLE", "ROLLING"]) in logback.groovy to the last line, after the appenders are declared
I changed the path of the log file from /var/log to home directory and the logs started appearing in console and log file. The reason for logs not being generated in /var/log directory is lack of permissions
Create a src/main/resources folder and place logback.groovy in it.
That worked for me with your project (cloned from Github) and the following logback file:
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.rolling.RollingFileAppender
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy
import static ch.qos.logback.classic.Level.DEBUG
appender("FILE", RollingFileAppender) {
file = "logFile.log"
rollingPolicy(TimeBasedRollingPolicy) {
fileNamePattern = "logFile.%d{yyyy-MM-dd}.log"
maxHistory = 30
}
encoder(PatternLayoutEncoder) {
pattern = "%-4relative [%thread] %-5level %logger{35} - %msg%n"
}
}
root(DEBUG, ["FILE"])
With logback/spring boot and a groovy config. The custom log configuration should be in a file named logback-spring.groovy instead of logback.groovy
The file should exist in src/main/resources.
see reference spring boot logging docs
I am using Gradle-2.11 and I am unable to find a way to create log files that logs debug level information. I don't want to do it through command line by redirecting the logs to the log file. I want Gradle code just like Apache Ant's 'record' task so that I can put that code in my build.gradle file wherever I want to create logs.
For ex: If I want to convert this ant task to gradle, then what would be the code:
<record name="${BuildLogPath}/${BuildLogFile}" append="no" loglevel="verbose" action="start"/>
Gradle integrates really nicely with Ant (https://docs.gradle.org/2.11/userguide/ant.html)
It doesn't automatically record each step. I didn't realize that is what you were asking. The updated below will produce the output and you can manually log.
ant.record(name: "${BuildLogPath}/${BuildLogFile}", append:false, loglevel: "verbose", action: "start")
ant.echo("start logging")
//... do stuff here
ant.echo(message: "end logging")
ant.record(name: "${BuildLogPath}/${BuildLogFile}", append:false, loglevel: "verbose", action: "stop")
This may do more of what you are asking. Note: This is something I adapted slightly from this excellent example:
http://themrsion.blogspot.com/2013/10/gradle-logging-writing-to-log-to-file.html
import org.gradle.logging.internal.*
String currentDate = new Date().format('yyyy-MMM-dd_HH-mm-ss-S')
String loggingDirectory = "${rootDir}/build/logs"
mkdir("${loggingDirectory}")
File gradleBuildLog = new File("${loggingDirectory}/${currentDate}_gradleBuild.log")
gradle.services.get(LoggingOutputInternal).addStandardOutputListener (new StandardOutputListener () {
void onOutput(CharSequence output) {
gradleBuildLog << output
}
})
gradle.services.get(LoggingOutputInternal).addStandardErrorListener (new StandardOutputListener () {
void onOutput(CharSequence output) {
gradleBuildLog << output
}
})
Grails is so nice to append the parameters sent to console output in the case of an error:
2011-05-23 12:17:05,173 [http-8080-5] ERROR errors.GrailsExceptionResolver - Exception occurred when processing request: [POST] / - parameters:
maps: on
maps: on
maps: on
maps:
_isPublic:
description: test
name: set1
isPublic: on
Stacktrace follows:
...
But how can I tell it to always show me which parameters are sent (like in Rails, for example)?
My current log4j configuration looks as follows:
error 'org.codehaus.groovy.grails.web.servlet', // controllers
'org.codehaus.groovy.grails.web.pages', // GSP
'org.codehaus.groovy.grails.web.sitemesh', // layouts
'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
'org.codehaus.groovy.grails.web.mapping', // URL mapping
'org.codehaus.groovy.grails.commons', // core / classloading
'org.codehaus.groovy.grails.plugins', // plugins
'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
'org.springframework',
'org.hibernate',
'net.sf.ehcache.hibernate'
warn 'org.mortbay.log'
I don't believe Grails itself supports logging the request parameters for each request. However, you can easily implement this yourself in a filter:
package com.example
class MyFilters {
private static final log = org.apache.commons.logging.LogFactory.getLog(this)
def filters = {
paramLogger(controller:'*', action:'*') {
before = {
log.debug "request params: $params"
}
}
}
}
Remember to enable debug logging for this filter in `Config.groovy by adding
debug com.example
to the log4j closure
You probably want to get more output from org.codehaus.groovy.grails.web so set that one to a lower level (debug or trace)