
org.neo4j.logging.FormattedLog Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of neo4j-logging Show documentation
Show all versions of neo4j-logging Show documentation
Logging interfaces and basic logger implementations.
/*
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.neo4j.logging;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import org.neo4j.function.Suppliers;
/**
* A {@link Log} implementation that applies a simple formatting to each log message.
*/
public class FormattedLog extends AbstractLog
{
static final Supplier DEFAULT_CURRENT_DATE_SUPPLIER = Date::new;
static final Function OUTPUT_STREAM_CONVERTER =
outputStream -> new PrintWriter( new OutputStreamWriter( outputStream, StandardCharsets.UTF_8 ) );
static final TimeZone UTC = TimeZone.getTimeZone( "UTC" );
/**
* A Builder for a {@link FormattedLog}
*/
public static class Builder
{
private TimeZone timezone = UTC;
private Object lock = this;
private String category = null;
private Level level = Level.INFO;
private boolean autoFlush = true;
private Builder()
{
}
/**
* Set the timezone for datestamps in the log
*
* @return this builder
*/
public Builder withUTCTimeZone()
{
return withTimeZone( UTC );
}
/**
* Set the timezone for datestamps in the log
*
* @param timezone the timezone to use for datestamps
* @return this builder
*/
public Builder withTimeZone( TimeZone timezone )
{
this.timezone = timezone;
return this;
}
/**
* Use the specified object to synchronize on.
*
* @param lock the object to synchronize on
* @return this builder
*/
public Builder usingLock( Object lock )
{
this.lock = lock;
return this;
}
/**
* Include the specified category in each output log line.
*
* @param category the category to include ing each output line
* @return this builder
*/
public Builder withCategory( String category )
{
this.category = category;
return this;
}
/**
* Use the specified log {@link Level} as a default.
*
* @param level the log level to use as a default
* @return this builder
*/
public Builder withLogLevel( Level level )
{
this.level = level;
return this;
}
/**
* Disable auto flushing.
*
* @return this builder
*/
public Builder withoutAutoFlush()
{
autoFlush = false;
return this;
}
/**
* Creates a {@link FormattedLog} instance that writes messages to an {@link OutputStream}.
*
* @param out An {@link OutputStream} to write to
* @return A {@link FormattedLog} instance that writes to the specified OutputStream
*/
public FormattedLog toOutputStream( OutputStream out )
{
return toPrintWriter( Suppliers.singleton( OUTPUT_STREAM_CONVERTER.apply( out ) ) );
}
/**
* Creates a {@link FormattedLog} instance that writes messages to {@link OutputStream}s obtained from the specified
* {@link Supplier}. The OutputStream is obtained from the Supplier before every log message is written.
*
* @param outSupplier A supplier for an output stream to write to
* @return A {@link FormattedLog} instance
*/
public FormattedLog toOutputStream( Supplier outSupplier )
{
return toPrintWriter( Suppliers.adapted( outSupplier, OUTPUT_STREAM_CONVERTER ) );
}
/**
* Creates a {@link FormattedLog} instance that writes messages to a {@link Writer}.
*
* @param writer A {@link Writer} to write to
* @return A {@link FormattedLog} instance that writes to the specified Writer
*/
public FormattedLog toWriter( Writer writer )
{
return toPrintWriter( new PrintWriter( writer ) );
}
/**
* Creates a {@link FormattedLog} instance that writes messages to a {@link PrintWriter}.
*
* @param writer A {@link PrintWriter} to write to
* @return A {@link FormattedLog} instance that writes to the specified PrintWriter
*/
public FormattedLog toPrintWriter( PrintWriter writer )
{
return toPrintWriter( Suppliers.singleton( writer ) );
}
/**
* Creates a {@link FormattedLog} instance that writes messages to {@link PrintWriter}s obtained from the specified
* {@link Supplier}. The PrintWriter is obtained from the Supplier before every log message is written.
*
* @param writerSupplier A supplier for a {@link PrintWriter} to write to
* @return A {@link FormattedLog} instance that writes to the specified PrintWriter
*/
public FormattedLog toPrintWriter( Supplier writerSupplier )
{
return new FormattedLog( DEFAULT_CURRENT_DATE_SUPPLIER, writerSupplier, timezone, lock, category, level, autoFlush );
}
}
private final Supplier currentDateSupplier;
private final Supplier writerSupplier;
private final TimeZone timezone;
private final Object lock;
private final String category;
private final AtomicReference levelRef;
private final boolean autoFlush;
private final Logger debugLogger;
private final Logger infoLogger;
private final Logger warnLogger;
private final Logger errorLogger;
/**
* Start creating a {@link FormattedLog} with UTC timezone for datestamps in the log
*
* @return a builder for a {@link FormattedLog}
*/
public static Builder withUTCTimeZone()
{
return new Builder().withUTCTimeZone();
}
/**
* Start creating a {@link FormattedLog} with the specified timezone for datestamps in the log
*
* @param timezone the timezone to use for datestamps
* @return a builder for a {@link FormattedLog}
*/
public static Builder withTimeZone( TimeZone timezone )
{
return new Builder().withTimeZone( timezone );
}
/**
* Start creating a {@link FormattedLog} using the specified object to synchronize on.
* Use {@link Builder#toOutputStream} to complete.
*
* @param lock the object to synchronize on
* @return a builder for a {@link FormattedLog}
*/
public static Builder usingLock( Object lock )
{
return new Builder().usingLock( lock );
}
/**
* Include the specified category in each output log line
*
* @param category the category to include ing each output line
* @return a builder for a {@link FormattedLog}
*/
public static Builder withCategory( String category )
{
return new Builder().withCategory( category );
}
/**
* Start creating a {@link FormattedLog} with the specified log {@link Level} as a default.
* Use {@link Builder#toOutputStream} to complete.
*
* @param level the log level to use as a default
* @return a builder for a {@link FormattedLog}
*/
public static Builder withLogLevel( Level level )
{
return new Builder().withLogLevel( level );
}
/**
* Start creating a {@link FormattedLog} without auto flushing.
* Use {@link Builder#toOutputStream} to complete.
*
* @return a builder for a {@link FormattedLog}
*/
public static Builder withoutAutoFlush()
{
return new Builder().withoutAutoFlush();
}
/**
* Creates a {@link FormattedLog} instance that writes messages to an {@link OutputStream}.
*
* @param out An {@link OutputStream} to write to
* @return A {@link FormattedLog} instance that writes to the specified OutputStream
*/
public static FormattedLog toOutputStream( OutputStream out )
{
return new Builder().toOutputStream( out );
}
/**
* Creates a {@link FormattedLog} instance that writes messages to {@link OutputStream}s obtained from the specified
* {@link Supplier}. The OutputStream is obtained from the Supplier before every log message is written.
*
* @param outSupplier A supplier for an output stream to write to
* @return A {@link FormattedLog} instance
*/
public static FormattedLog toOutputStream( Supplier outSupplier )
{
return new Builder().toOutputStream( outSupplier );
}
/**
* Creates a {@link FormattedLog} instance that writes messages to a {@link Writer}.
*
* @param writer A {@link Writer} to write to
* @return A {@link FormattedLog} instance that writes to the specified Writer
*/
public static FormattedLog toWriter( Writer writer )
{
return new Builder().toWriter( writer );
}
/**
* Creates a {@link FormattedLog} instance that writes messages to a {@link PrintWriter}.
*
* @param writer A {@link PrintWriter} to write to
* @return A {@link FormattedLog} instance that writes to the specified PrintWriter
*/
public static FormattedLog toPrintWriter( PrintWriter writer )
{
return new Builder().toPrintWriter( writer );
}
/**
* Creates a {@link FormattedLog} instance that writes messages to {@link PrintWriter}s obtained from the specified
* {@link Supplier}. The PrintWriter is obtained from the Supplier before every log message is written.
*
* @param writerSupplier A supplier for a {@link PrintWriter} to write to
* @return A {@link FormattedLog} instance that writes to the specified PrintWriter
*/
public static FormattedLog toPrintWriter( Supplier writerSupplier )
{
return new Builder().toPrintWriter( writerSupplier );
}
protected FormattedLog(
Supplier currentDateSupplier,
Supplier writerSupplier,
TimeZone timezone,
Object maybeLock,
String category,
Level level,
boolean autoFlush )
{
this.currentDateSupplier = currentDateSupplier;
this.writerSupplier = writerSupplier;
this.timezone = timezone;
this.lock = ( maybeLock != null ) ? maybeLock : this;
this.category = category;
this.levelRef = new AtomicReference<>( level );
this.autoFlush = autoFlush;
String debugPrefix = ( category != null && !category.isEmpty() ) ? "DEBUG [" + category + "]" : "DEBUG";
String infoPrefix = ( category != null && !category.isEmpty() ) ? "INFO [" + category + "]" : "INFO ";
String warnPrefix = ( category != null && !category.isEmpty() ) ? "WARN [" + category + "]" : "WARN ";
String errorPrefix = ( category != null && !category.isEmpty() ) ? "ERROR [" + category + "]" : "ERROR";
this.debugLogger = new FormattedLogger( writerSupplier, debugPrefix );
this.infoLogger = new FormattedLogger( writerSupplier, infoPrefix );
this.warnLogger = new FormattedLogger( writerSupplier, warnPrefix );
this.errorLogger = new FormattedLogger( writerSupplier, errorPrefix );
}
/**
* Get the current {@link Level} that logging is enabled at
*
* @return the current level that logging is enabled at
*/
public Level getLevel()
{
return levelRef.get();
}
/**
* Set the {@link Level} that logging should be enabled at
*
* @param level the new logging level
* @return the previous logging level
*/
public Level setLevel( Level level )
{
return levelRef.getAndSet( level );
}
@Override
public boolean isDebugEnabled()
{
return Level.DEBUG.compareTo( levelRef.get() ) >= 0;
}
@Nonnull
@Override
public Logger debugLogger()
{
return isDebugEnabled() ? this.debugLogger : NullLogger.getInstance();
}
/**
* @return true if the current log level enables info logging
*/
public boolean isInfoEnabled()
{
return Level.INFO.compareTo( levelRef.get() ) >= 0;
}
@Nonnull
@Override
public Logger infoLogger()
{
return isInfoEnabled() ? this.infoLogger : NullLogger.getInstance();
}
/**
* @return true if the current log level enables warn logging
*/
public boolean isWarnEnabled()
{
return Level.WARN.compareTo( levelRef.get() ) >= 0;
}
@Nonnull
@Override
public Logger warnLogger()
{
return isWarnEnabled() ? this.warnLogger : NullLogger.getInstance();
}
/**
* @return true if the current log level enables error logging
*/
public boolean isErrorEnabled()
{
return Level.ERROR.compareTo( levelRef.get() ) >= 0;
}
@Nonnull
@Override
public Logger errorLogger()
{
return isErrorEnabled() ? this.errorLogger : NullLogger.getInstance();
}
@Override
public void bulk( @Nonnull Consumer consumer )
{
PrintWriter writer;
synchronized (lock)
{
writer = writerSupplier.get();
consumer.accept( new FormattedLog( currentDateSupplier, Suppliers.singleton( writer ), timezone, lock, category, levelRef.get(), false ) );
}
if ( autoFlush )
{
writer.flush();
}
}
private class FormattedLogger extends AbstractPrintWriterLogger
{
private final String prefix;
private final DateFormat format;
public FormattedLogger( @Nonnull Supplier writerSupplier, @Nonnull String prefix )
{
super( writerSupplier, lock, autoFlush );
this.prefix = prefix;
format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSSZ" );
format.setTimeZone( timezone );
}
@Override
protected void writeLog( @Nonnull PrintWriter out, @Nonnull String message )
{
lineStart( out );
out.write( message );
out.println();
}
@Override
protected void writeLog( @Nonnull PrintWriter out, @Nonnull String message, @Nonnull Throwable throwable )
{
lineStart( out );
out.write( message );
if ( throwable.getMessage() != null )
{
out.write( ' ' );
out.write( throwable.getMessage() );
}
out.println();
throwable.printStackTrace( out );
}
@Override
protected Logger getBulkLogger( @Nonnull PrintWriter out, @Nonnull Object lock )
{
return new FormattedLogger( Suppliers.singleton( out ), prefix );
}
private void lineStart( PrintWriter out )
{
out.write( time() );
out.write( ' ' );
out.write( prefix );
out.write( ' ' );
}
private String time()
{
return format.format( currentDateSupplier.get() );
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy