Skip to content

Commit

Permalink
Temporary workaround to avoid debug logging from Netty.
Browse files Browse the repository at this point in the history
- Because this will go into patch release, silence stdout while Netty
  logging loads. In next major or minor release, we'll fix this by
  either removing Slf4j from the classpath or configuring it properly.
  • Loading branch information
jakewins committed Oct 29, 2015
1 parent 5043b53 commit ea3dee3
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 7 deletions.
29 changes: 23 additions & 6 deletions community/kernel/src/test/java/org/neo4j/test/SuppressOutput.java
Expand Up @@ -25,8 +25,9 @@

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
Expand All @@ -37,6 +38,9 @@
import java.util.logging.SimpleFormatter;
import java.util.logging.StreamHandler;

import static java.lang.System.lineSeparator;
import static java.util.Arrays.asList;

/**
* Suppresses outputs such as System.out, System.err and java.util.logging for example when running a test.
* It's also a {@link TestRule} which makes it fit in nicely in JUnit.
Expand Down Expand Up @@ -103,7 +107,7 @@ void restore( boolean failure ) throws IOException

public static final Suppressible java_util_logging = java_util_logging( new ByteArrayOutputStream(), null );

public static Suppressible java_util_logging( final OutputStream redirectTo, Level level )
public static Suppressible java_util_logging( final ByteArrayOutputStream redirectTo, Level level )
{
final Handler replacement = redirectTo == null ? null : new StreamHandler( redirectTo, new SimpleFormatter() );
if ( replacement != null && level != null )
Expand Down Expand Up @@ -149,7 +153,7 @@ void restore( boolean failure ) throws IOException

public <T> T call( Callable<T> callable ) throws Exception
{
Voice[] voices = captureVoices();
voices = captureVoices();
boolean failure = true;
try
{
Expand Down Expand Up @@ -231,9 +235,9 @@ public interface Suppressible
public static abstract class Voice
{
private Suppressible suppressible;
private OutputStream voiceStream;
private ByteArrayOutputStream voiceStream;

public Voice(Suppressible suppressible, OutputStream originalStream)
public Voice(Suppressible suppressible, ByteArrayOutputStream originalStream)
{
this.suppressible = suppressible;
this.voiceStream = originalStream;
Expand All @@ -249,10 +253,23 @@ public boolean containsMessage( String message )
return voiceStream.toString().contains( message );
}

/** Get each line written to this voice since it was suppressed */
public List<String> lines()
{
return asList( toString().split( lineSeparator() ) );
}

@Override
public String toString()
{
return voiceStream.toString();
try
{
return voiceStream.toString( StandardCharsets.UTF_8.name() );
}
catch ( UnsupportedEncodingException e )
{
throw new RuntimeException( e );
}
}

abstract void restore( boolean failure ) throws IOException;
Expand Down
Expand Up @@ -39,6 +39,7 @@
import org.neo4j.server.configuration.ServerSettings;
import org.neo4j.server.logging.JULBridge;
import org.neo4j.server.logging.JettyLogBridge;
import org.neo4j.server.logging.Netty4LogBridge;
import org.neo4j.server.logging.Netty4LoggerFactory;

import static java.lang.String.format;
Expand Down Expand Up @@ -81,7 +82,7 @@ public int start( File configFile, Pair<String, String> ... configOverrides )
Logger.getLogger( "" ).setLevel( Level.WARNING );
JULBridge.forwardTo( userLogProvider );
JettyLogBridge.setLogProvider( userLogProvider );
InternalLoggerFactory.setDefaultFactory( new Netty4LoggerFactory( userLogProvider ) );
Netty4LogBridge.setLogProvider( userLogProvider );

log = userLogProvider.getLog( getClass() );

Expand Down
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2002-2015 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.server.logging;

import io.netty.util.internal.logging.InternalLoggerFactory;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;

import org.neo4j.logging.LogProvider;

/**
* Glue code for swapping in Neo4j logging into Netty4
*/
public class Netty4LogBridge
{
private static final PrintStream NULL_OUTPUT = new PrintStream( new NullOutput() );

public static void setLogProvider( LogProvider logProvider )
{
// TODO: Undo below hack in next minor/major release
// Netty 4 will look for and use Slf4j if it's on the classpath. In this release (2.3.x),
// it is on the classpath, via `logback-classic`. However, we do not configure logback,
// meaning some debug output leaks to stdout before we replace the logging below.
// This should be fixed properly in the next release that is not a patch release.
PrintStream originalStdOut = System.out;
try
{
System.setOut( NULL_OUTPUT );
InternalLoggerFactory.setDefaultFactory( new Netty4LoggerFactory( logProvider ) );
}
finally
{
System.setOut( originalStdOut );
}

}

private static class NullOutput extends OutputStream
{
@Override
public void write( int b ) throws IOException
{
// no-op
}
}
}
@@ -0,0 +1,132 @@
/*
* Copyright (c) 2002-2015 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.server.integration;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Test;

import java.util.List;
import java.util.concurrent.Callable;

import org.neo4j.server.CommunityBootstrapper;
import org.neo4j.test.SuppressOutput;
import org.neo4j.test.server.ExclusiveServerTestBase;

import static java.util.Arrays.asList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;

public class StartupLoggingIT extends ExclusiveServerTestBase
{
@Test
public void shouldLogHelpfulStartupMessages() throws Throwable
{
// Given
SuppressOutput suppressed = SuppressOutput.suppressAll();

// When
suppressed.call( new Callable<Object>()
{
@Override
public Object call() throws Exception
{
CommunityBootstrapper boot = new CommunityBootstrapper();
CommunityBootstrapper.start( boot, new String[]{} );
boot.stop();
return null;
}
});

// Then
List<String> captured = suppressed.getOutputVoice().lines();
// TODO: Obviously the logging below is insane, but we added this test in a point release, so we don't want to break anyone grepping for this
// This should be changed in 3.0.0.
assertThat( captured, matchesLines(
warn( "Config file \\[config/neo4j-server.properties\\] does not exist." ),
warn( "Config file \\[config/neo4j.properties\\] does not exist." ),
info( "Successfully started database" ),
info( "Starting HTTP on port 7474 \\(.+ threads available\\)" ),
info( "Mounting static content at /webadmin" ),
info( "Mounting static content at /browser" ),
info( "Remote interface ready and available at http://.+:7474/" ),

info( "Successfully shutdown Neo4j Server" ),
info( "Successfully stopped database" ),
info( "Successfully shutdown database" ),
info( "Successfully shutdown Neo Server on port \\[.+\\], database \\[.+\\]")
) );
}

public static Matcher<List<String>> matchesLines( final Matcher<String> ... lineMatchers )
{
return new TypeSafeMatcher<List<String>>()
{
@Override
protected boolean matchesSafely( List<String> lines )
{
if(lineMatchers.length != lines.size())
{
return false;
}

for ( int i = 0; i < lines.size(); i++ )
{
if( !lineMatchers[i].matches( lines.get( i ) ) )
{
return false;
}
}
return true;
}

@Override
public void describeTo( Description description )
{
description.appendList( "", "\n", "", asList(lineMatchers) );
}
};
}

public static Matcher<String> info( String messagePattern ) { return line("INFO", messagePattern); }
public static Matcher<String> warn( String messagePattern ) { return line("WARN", messagePattern); }

public static Matcher<String> line( final String level, final String messagePattern )
{
return new TypeSafeMatcher<String>()
{
@Override
protected boolean matchesSafely( String line )
{
// eg. 2015-10-27 15:44:18.049-0500 INFO Successfully started database
// Assert rather than return boolean, to get the exact line in the error output
assertTrue( "[" + line + "] should match pattern [" + messagePattern + "]", line.matches( ".*" + level + "\\s+" + messagePattern ));
return true;
}

@Override
public void describeTo( Description description )
{
description.appendText( level ).appendText( " " ).appendText( messagePattern );
}
};
}
}

0 comments on commit ea3dee3

Please sign in to comment.