/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.beam.runners.dataflow.options; import static com.google.common.base.Preconditions.checkNotNull; import com.fasterxml.jackson.annotation.JsonCreator; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.apache.beam.sdk.options.Default; import org.apache.beam.sdk.options.Description; import org.apache.beam.sdk.options.PipelineOptions; /** * Options that are used to control logging configuration on the Dataflow worker. */ @Description("Options that are used to control logging configuration on the Dataflow worker.") public interface DataflowWorkerLoggingOptions extends PipelineOptions { /** * The set of log levels that can be used on the Dataflow worker. */ enum Level { /** Special level used to turn off logging. */ OFF, /** Level for logging error messages. */ ERROR, /** Level for logging warning messages. */ WARN, /** Level for logging informational messages. */ INFO, /** Level for logging diagnostic messages. */ DEBUG, /** Level for logging tracing messages. */ TRACE } /** * This option controls the default log level of all loggers without a log level override. */ @Description("Controls the default log level of all loggers without a log level override.") @Default.Enum("INFO") Level getDefaultWorkerLogLevel(); void setDefaultWorkerLogLevel(Level level); /** * Controls the log level given to messages printed to {@code System.out}. * * <p>Note that the message may be filtered depending on the * {@link #getDefaultWorkerLogLevel defaultWorkerLogLevel} or if a {@code System.out} override is * specified via {@link #getWorkerLogLevelOverrides workerLogLevelOverrides}. */ @Description("Controls the log level given to messages printed to System.out. Note that the " + "message may be filtered depending on the defaultWorkerLogLevel or if a 'System.out' " + "override is specified via workerLogLevelOverrides.") @Default.Enum("INFO") Level getWorkerSystemOutMessageLevel(); void setWorkerSystemOutMessageLevel(Level level); /** * Controls the log level given to messages printed to {@code System.err}. * * <p>Note that the message may be filtered depending on the * {@link #getDefaultWorkerLogLevel defaultWorkerLogLevel} or if a {@code System.err} override is * specified via {@link #getWorkerLogLevelOverrides workerLogLevelOverrides}. */ @Description("Controls the log level given to messages printed to System.err. Note that the " + "message may be filtered depending on the defaultWorkerLogLevel or if a 'System.err' " + "override is specified via workerLogLevelOverrides.") @Default.Enum("ERROR") Level getWorkerSystemErrMessageLevel(); void setWorkerSystemErrMessageLevel(Level level); /** * This option controls the log levels for specifically named loggers. * * <p>Later options with equivalent names override earlier options. * * <p>See {@link WorkerLogLevelOverrides} for more information on how to configure logging * on a per {@link Class}, {@link Package}, or name basis. If used from the command line, * the expected format is {"Name":"Level",...}, further details on * {@link WorkerLogLevelOverrides#from}. */ @Description("This option controls the log levels for specifically named loggers. " + "The expected format is {\"Name\":\"Level\",...}. The Dataflow worker supports a logging " + "hierarchy based off of names that are '.' separated. For example, by specifying the value " + "{\"a.b.c.Foo\":\"DEBUG\"}, the logger for the class 'a.b.c.Foo' will be configured to " + "output logs at the DEBUG level. Similarly, by specifying the value {\"a.b.c\":\"WARN\"}, " + "all loggers underneath the 'a.b.c' package will be configured to output logs at the WARN " + "level. System.out and System.err levels are configured via loggers of the corresponding " + "name. Also, note that when multiple overrides are specified, the exact name followed by " + "the closest parent takes precedence.") WorkerLogLevelOverrides getWorkerLogLevelOverrides(); void setWorkerLogLevelOverrides(WorkerLogLevelOverrides value); /** * Defines a log level override for a specific class, package, or name. * * <p>The Dataflow worker harness supports a logging hierarchy based off of names that are "." * separated. It is a common pattern to have the logger for a given class share the same name as * the class itself. Given the classes {@code a.b.c.Foo}, {@code a.b.c.Xyz}, and {@code a.b.Bar}, * with loggers named {@code "a.b.c.Foo"}, {@code "a.b.c.Xyz"}, and {@code "a.b.Bar"} * respectively, we can override the log levels: * <ul> * <li>for {@code Foo} by specifying the name {@code "a.b.c.Foo"} or the {@link Class} * representing {@code a.b.c.Foo}. * <li>for {@code Foo}, {@code Xyz}, and {@code Bar} by specifying the name {@code "a.b"} or * the {@link Package} representing {@code a.b}. * <li>for {@code Foo} and {@code Bar} by specifying both of their names or classes. * </ul> * * <p>{@code System.out} and {@code System.err} messages are configured via loggers of the * corresponding name. Note that by specifying multiple overrides, the exact name followed by the * closest parent takes precedence. */ class WorkerLogLevelOverrides extends HashMap<String, Level> { /** * Overrides the default log level for the passed in class. * * <p>This is equivalent to calling * {@link #addOverrideForName(String, DataflowWorkerLoggingOptions.Level)} * and passing in the {@link Class#getName() class name}. */ public WorkerLogLevelOverrides addOverrideForClass(Class<?> klass, Level level) { checkNotNull(klass, "Expected class to be not null."); addOverrideForName(klass.getName(), level); return this; } /** * Overrides the default log level for the passed in package. * * <p>This is equivalent to calling * {@link #addOverrideForName(String, DataflowWorkerLoggingOptions.Level)} * and passing in the {@link Package#getName() package name}. */ public WorkerLogLevelOverrides addOverrideForPackage(Package pkg, Level level) { checkNotNull(pkg, "Expected package to be not null."); addOverrideForName(pkg.getName(), level); return this; } /** * Overrides the default log level for the passed in name. * * <p>Note that because of the hierarchical nature of logger names, this will * override the log level of all loggers that have the passed in name or * a parent logger that has the passed in name. */ public WorkerLogLevelOverrides addOverrideForName(String name, Level level) { checkNotNull(name, "Expected name to be not null."); checkNotNull(level, "Expected level to be one of %s.", Arrays.toString(Level.values())); put(name, level); return this; } /** * Expects a map keyed by logger {@code Name}s with values representing {@code Level}s. * The {@code Name} generally represents the fully qualified Java * {@link Class#getName() class name}, or fully qualified Java * {@link Package#getName() package name}, or custom logger name. The {@code Level} * represents the log level and must be one of {@link Level}. */ @JsonCreator public static WorkerLogLevelOverrides from(Map<String, String> values) { checkNotNull(values, "Expected values to be not null."); WorkerLogLevelOverrides overrides = new WorkerLogLevelOverrides(); for (Map.Entry<String, String> entry : values.entrySet()) { try { overrides.addOverrideForName(entry.getKey(), Level.valueOf(entry.getValue())); } catch (IllegalArgumentException e) { throw new IllegalArgumentException(String.format( "Unsupported log level '%s' requested for %s. Must be one of %s.", entry.getValue(), entry.getKey(), Arrays.toString(Level.values()))); } } return overrides; } } }