1616
1717package com .google .cloud .dataflow .sdk .runners .worker .logging ;
1818
19+ import static com .google .cloud .dataflow .sdk .options .DataflowWorkerLoggingOptions .Level .DEBUG ;
20+ import static com .google .cloud .dataflow .sdk .options .DataflowWorkerLoggingOptions .Level .ERROR ;
21+ import static com .google .cloud .dataflow .sdk .options .DataflowWorkerLoggingOptions .Level .INFO ;
22+ import static com .google .cloud .dataflow .sdk .options .DataflowWorkerLoggingOptions .Level .TRACE ;
23+ import static com .google .cloud .dataflow .sdk .options .DataflowWorkerLoggingOptions .Level .WARN ;
24+
25+ import com .google .api .client .util .Lists ;
26+ import com .google .cloud .dataflow .sdk .options .DataflowWorkerLoggingOptions ;
27+ import com .google .cloud .dataflow .sdk .options .DataflowWorkerLoggingOptions .WorkerLogLevelOverride ;
1928import com .google .common .collect .ImmutableBiMap ;
2029
2130import java .io .File ;
2231import java .io .IOException ;
32+ import java .util .List ;
2333import java .util .logging .FileHandler ;
2434import java .util .logging .Formatter ;
2535import java .util .logging .Handler ;
2838import java .util .logging .Logger ;
2939
3040/**
31- * Sets up java.util.Logging configuration on the Dataflow Worker Harness with a
32- * console and file logger. The console and file loggers use the
33- * {@link DataflowWorkerLoggingFormatter} format. A user can override
34- * the logging level and location by specifying the Java system properties
35- * "dataflow.worker.logging.level" and "dataflow.worker.logging.location" respectively.
36- * The default log level is INFO and the default location is a file named dataflow-worker.log
37- * within the systems temporary directory.
41+ * Sets up {@link java.util.logging} configuration on the Dataflow worker with a
42+ * file logger. The file logger uses the {@link DataflowWorkerLoggingFormatter} format.
43+ * A user can override the logging level by customizing the options found within
44+ * {@link DataflowWorkerLoggingOptions}. A user can override the location by specifying the
45+ * Java system property "dataflow.worker.logging.location". The default log level is INFO
46+ * and the default location is a file named dataflow-worker.log within the systems temporary
47+ * directory.
3848 */
3949public class DataflowWorkerLoggingInitializer {
4050 private static final String DEFAULT_LOGGING_LOCATION =
4151 new File (System .getProperty ("java.io.tmpdir" ), "dataflow-worker.log" ).getPath ();
4252 private static final String ROOT_LOGGER_NAME = "" ;
43- public static final String DATAFLOW_WORKER_LOGGING_LEVEL = "dataflow.worker.logging.level" ;
44- public static final String DATAFLOW_WORKER_LOGGING_LOCATION = "dataflow.worker.logging.location" ;
45- public static final ImmutableBiMap <Level , String > LEVELS =
46- ImmutableBiMap .<Level , String >builder ()
47- .put (Level .SEVERE , "ERROR" )
48- .put (Level .WARNING , "WARNING" )
49- .put (Level .INFO , "INFO" )
50- .put (Level .FINE , "DEBUG" )
51- .put (Level .FINEST , "TRACE" )
53+ private static final String DATAFLOW_WORKER_LOGGING_LOCATION = "dataflow.worker.logging.location" ;
54+ static final ImmutableBiMap <Level , DataflowWorkerLoggingOptions .Level > LEVELS =
55+ ImmutableBiMap .<Level , DataflowWorkerLoggingOptions .Level >builder ()
56+ .put (Level .SEVERE , ERROR )
57+ .put (Level .WARNING , WARN )
58+ .put (Level .INFO , INFO )
59+ .put (Level .FINE , DEBUG )
60+ .put (Level .FINEST , TRACE )
5261 .build ();
53- private static final String DEFAULT_LOG_LEVEL = LEVELS .get (Level .INFO );
5462
55- public void initialize () {
56- initialize (LogManager .getLogManager ());
57- }
63+ /**
64+ * This default log level is overridden by the log level found at
65+ * {@code DataflowWorkerLoggingOptions#getDefaultWorkerLogLevel()}.
66+ */
67+ private static final DataflowWorkerLoggingOptions .Level DEFAULT_LOG_LEVEL =
68+ LEVELS .get (Level .INFO );
69+
70+ /* We need to store a reference to the configured loggers so that they are not
71+ * garbage collected. java.util.logging only has weak references to the loggers
72+ * so if they are garbage collection, our hierarchical configuration will be lost. */
73+ private static List <Logger > configuredLoggers = Lists .newArrayList ();
74+ private static FileHandler fileHandler ;
5875
59- void initialize (LogManager logManager ) {
76+ /**
77+ * Sets up the initial logging configuration.
78+ */
79+ public static synchronized void initialize () {
80+ if (fileHandler != null ) {
81+ return ;
82+ }
6083 try {
61- Level logLevel = LEVELS .inverse ().get (
62- System .getProperty (DATAFLOW_WORKER_LOGGING_LEVEL , DEFAULT_LOG_LEVEL ));
84+ Level logLevel = LEVELS .inverse ().get (DEFAULT_LOG_LEVEL );
6385 Formatter formatter = new DataflowWorkerLoggingFormatter ();
6486
65- FileHandler fileHandler = new FileHandler (
87+ fileHandler = new FileHandler (
6688 System .getProperty (DATAFLOW_WORKER_LOGGING_LOCATION , DEFAULT_LOGGING_LOCATION ),
6789 true /* Append so that we don't squash existing logs */ );
6890 fileHandler .setFormatter (formatter );
69- fileHandler .setLevel (logLevel );
91+ fileHandler .setLevel (Level . ALL );
7092
7193 // Reset the global log manager, get the root logger and remove the default log handlers.
94+ LogManager logManager = LogManager .getLogManager ();
7295 logManager .reset ();
7396 Logger rootLogger = logManager .getLogger (ROOT_LOGGER_NAME );
7497 for (Handler handler : rootLogger .getHandlers ()) {
@@ -81,4 +104,34 @@ void initialize(LogManager logManager) {
81104 throw new ExceptionInInitializerError (e );
82105 }
83106 }
107+
108+ /**
109+ * Reconfigures logging with the passed in options.
110+ */
111+ public static synchronized void configure (DataflowWorkerLoggingOptions options ) {
112+ initialize ();
113+ if (options .getDefaultWorkerLogLevel () != null ) {
114+ LogManager .getLogManager ().getLogger (ROOT_LOGGER_NAME ).setLevel (
115+ LEVELS .inverse ().get (options .getDefaultWorkerLogLevel ()));
116+ }
117+ /* We store a reference to all the custom loggers the user configured.
118+ * To make sure that these custom levels override the default logger level,
119+ * we break the parent chain and have the logger directly pass log records
120+ * to the file handler. */
121+ if (options .getWorkerLogLevelOverrides () != null ) {
122+ for (WorkerLogLevelOverride loggerOverride : options .getWorkerLogLevelOverrides ()) {
123+ Logger logger = Logger .getLogger (loggerOverride .getName ());
124+ logger .setUseParentHandlers (false );
125+ logger .setLevel (LEVELS .inverse ().get (loggerOverride .getLevel ()));
126+ logger .addHandler (fileHandler );
127+ configuredLoggers .add (logger );
128+ }
129+ }
130+ }
131+
132+ // Visible for testing
133+ static void reset () {
134+ configuredLoggers = Lists .newArrayList ();
135+ fileHandler = null ;
136+ }
84137}
0 commit comments