| /* |
| * 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.druid.java.util.common.logger; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.base.Preconditions; |
| import org.apache.druid.java.util.common.StringUtils; |
| import org.apache.druid.timeline.DataSegment; |
| import org.apache.druid.timeline.SegmentId; |
| import org.slf4j.LoggerFactory; |
| |
| import javax.annotation.Nullable; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.function.BiConsumer; |
| import java.util.stream.Stream; |
| |
| public class Logger |
| { |
| @VisibleForTesting |
| static final int SEGMENTS_PER_LOG_MESSAGE = 64; |
| |
| private final org.slf4j.Logger log; |
| private final boolean stackTraces; |
| private final Logger noStackTraceLogger; |
| |
| public Logger(String name) |
| { |
| this(LoggerFactory.getLogger(name), true); |
| } |
| |
| public Logger(Class clazz) |
| { |
| this(LoggerFactory.getLogger(clazz), true); |
| } |
| |
| protected Logger(org.slf4j.Logger log, boolean stackTraces) |
| { |
| this.log = log; |
| this.stackTraces = stackTraces; |
| noStackTraceLogger = stackTraces ? new Logger(log, false) : this; |
| } |
| |
| protected org.slf4j.Logger getSlf4jLogger() |
| { |
| return log; |
| } |
| |
| @Override |
| public String toString() |
| { |
| return StringUtils.format("Logger{name=[%s], class[%s]}", log.getName(), log.getClass()); |
| } |
| |
| /** |
| * Returns a copy of this Logger that does not log exception stack traces, unless the log level is DEBUG or lower. |
| * Useful for writing code like: {@code log.noStackTrace().warn(e, "Something happened.");} |
| */ |
| public Logger noStackTrace() |
| { |
| return noStackTraceLogger; |
| } |
| |
| public void trace(String message, Object... formatArgs) |
| { |
| if (log.isTraceEnabled()) { |
| log.trace(StringUtils.nonStrictFormat(message, formatArgs)); |
| } |
| } |
| |
| public void debug(String message, Object... formatArgs) |
| { |
| if (log.isDebugEnabled()) { |
| log.debug(StringUtils.nonStrictFormat(message, formatArgs)); |
| } |
| } |
| |
| public void debug(Throwable t, String message, Object... formatArgs) |
| { |
| if (log.isDebugEnabled()) { |
| logException(log::debug, t, StringUtils.nonStrictFormat(message, formatArgs)); |
| } |
| } |
| |
| public void info(String message, Object... formatArgs) |
| { |
| if (log.isInfoEnabled()) { |
| log.info(StringUtils.nonStrictFormat(message, formatArgs)); |
| } |
| } |
| |
| public void info(Throwable t, String message, Object... formatArgs) |
| { |
| if (log.isInfoEnabled()) { |
| logException(log::info, t, StringUtils.nonStrictFormat(message, formatArgs)); |
| } |
| } |
| |
| /** |
| * Protect against assuming slf4j convention. use `warn(Throwable t, String message, Object... formatArgs)` instead |
| * |
| * @param message The string message |
| * @param t The Throwable to log |
| */ |
| @Deprecated |
| public void warn(String message, Throwable t) |
| { |
| warn(t, message); |
| } |
| |
| public void warn(String message, Object... formatArgs) |
| { |
| log.warn(StringUtils.nonStrictFormat(message, formatArgs)); |
| } |
| |
| public void warn(Throwable t, String message, Object... formatArgs) |
| { |
| logException(log::warn, t, StringUtils.nonStrictFormat(message, formatArgs)); |
| } |
| |
| public void error(String message, Object... formatArgs) |
| { |
| log.error(StringUtils.nonStrictFormat(message, formatArgs)); |
| } |
| |
| /** |
| * Protect against assuming slf4j convention. use `error(Throwable t, String message, Object... formatArgs)` instead |
| * |
| * @param message The string message |
| * @param t The Throwable to log |
| */ |
| @Deprecated |
| public void error(String message, Throwable t) |
| { |
| error(t, message); |
| } |
| |
| public void error(Throwable t, String message, Object... formatArgs) |
| { |
| logException(log::error, t, StringUtils.nonStrictFormat(message, formatArgs)); |
| } |
| |
| public void assertionError(String message, Object... formatArgs) |
| { |
| log.error("ASSERTION_ERROR: " + message, formatArgs); |
| } |
| |
| public void debugSegments(@Nullable final Collection<DataSegment> segments, @Nullable String preamble) |
| { |
| if (log.isDebugEnabled()) { |
| logSegments(this::debug, segments, preamble); |
| } |
| } |
| |
| public void infoSegments(@Nullable final Collection<DataSegment> segments, @Nullable String preamble) |
| { |
| if (log.isInfoEnabled()) { |
| logSegments(this::info, segments, preamble); |
| } |
| } |
| |
| public void infoSegmentIds(@Nullable final Stream<SegmentId> segments, @Nullable String preamble) |
| { |
| if (log.isInfoEnabled()) { |
| logSegmentIds(this::info, segments, preamble); |
| } |
| } |
| |
| public void warnSegments(@Nullable final Collection<DataSegment> segments, @Nullable String preamble) |
| { |
| if (log.isWarnEnabled()) { |
| logSegments(this::warn, segments, preamble); |
| } |
| } |
| |
| public void errorSegments(@Nullable final Collection<DataSegment> segments, @Nullable String preamble) |
| { |
| logSegments(this::error, segments, preamble); |
| } |
| |
| public boolean isTraceEnabled() |
| { |
| return log.isTraceEnabled(); |
| } |
| |
| public boolean isDebugEnabled() |
| { |
| return log.isDebugEnabled(); |
| } |
| |
| public boolean isInfoEnabled() |
| { |
| return log.isInfoEnabled(); |
| } |
| |
| private void logException(BiConsumer<String, Throwable> fn, Throwable t, String message) |
| { |
| if (stackTraces || log.isDebugEnabled()) { |
| fn.accept(message, t); |
| } else { |
| if (message.isEmpty()) { |
| fn.accept(t.toString(), null); |
| } else { |
| fn.accept(StringUtils.nonStrictFormat("%s (%s)", message, t.toString()), null); |
| } |
| } |
| } |
| |
| /** |
| * Logs all the segment ids you could ever want, {@link #SEGMENTS_PER_LOG_MESSAGE} at a time, as a comma separated |
| * list. |
| */ |
| @VisibleForTesting |
| static void logSegments( |
| Logger.LogFunction logger, |
| @Nullable final Collection<DataSegment> segments, |
| @Nullable String preamble |
| ) |
| { |
| if (segments == null || segments.isEmpty()) { |
| return; |
| } |
| logSegmentIds(logger, segments.stream().map(DataSegment::getId), preamble); |
| } |
| |
| /** |
| * Logs all the segment ids you could ever want, {@link #SEGMENTS_PER_LOG_MESSAGE} at a time, as a comma separated |
| * list. |
| */ |
| @VisibleForTesting |
| static void logSegmentIds( |
| Logger.LogFunction logger, |
| @Nullable final Stream<SegmentId> stream, |
| @Nullable String preamble |
| ) |
| { |
| Preconditions.checkNotNull(preamble); |
| if (stream == null) { |
| return; |
| } |
| final Iterator<SegmentId> iterator = stream.iterator(); |
| if (!iterator.hasNext()) { |
| return; |
| } |
| final String logFormat = preamble + ": %s"; |
| |
| int counter = 0; |
| StringBuilder sb = null; |
| while (iterator.hasNext()) { |
| SegmentId nextId = iterator.next(); |
| if (counter == 0) { |
| // use segmentId string length of first as estimate for total size of builder for this batch |
| sb = new StringBuilder(SEGMENTS_PER_LOG_MESSAGE * (2 + nextId.safeUpperLimitOfStringSize())).append("["); |
| } |
| sb.append(nextId); |
| if (++counter < SEGMENTS_PER_LOG_MESSAGE && iterator.hasNext()) { |
| sb.append(", "); |
| } |
| counter = counter % SEGMENTS_PER_LOG_MESSAGE; |
| if (counter == 0) { |
| // flush |
| sb.append("]"); |
| logger.log(logFormat, sb.toString()); |
| } |
| } |
| |
| // check for stragglers |
| if (counter > 0) { |
| sb.append("]"); |
| logger.log(logFormat, sb.toString()); |
| } |
| } |
| |
| @FunctionalInterface |
| public interface LogFunction |
| { |
| void log(String msg, Object... format); |
| } |
| } |