Skip to content

Commit

Permalink
Split QueryLogger into its own source file
Browse files Browse the repository at this point in the history
  • Loading branch information
thobe committed Apr 4, 2017
1 parent bc5cc71 commit 4c83502
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 196 deletions.
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2002-2017 "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 Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.query;

import org.neo4j.graphdb.config.Setting;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.kernel.configuration.Config;

enum QueryLogEntryContent
{
LOG_PARAMETERS( GraphDatabaseSettings.log_queries_parameter_logging_enabled ),
LOG_DETAILED_TIME( GraphDatabaseSettings.log_queries_detailed_time_logging_enabled ),
LOG_ALLOCATED_BYTES( GraphDatabaseSettings.log_queries_allocation_logging_enabled ),
LOG_PAGE_DETAILS( GraphDatabaseSettings.log_queries_page_detail_logging_enabled );
private final Setting<Boolean> setting;

QueryLogEntryContent( Setting<Boolean> setting )
{
this.setting = setting;
}

boolean enabledIn( Config config )
{
return config.get( setting );
}
}
@@ -0,0 +1,105 @@
/*
* Copyright (c) 2002-2017 "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 Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.query;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;

import org.neo4j.helpers.Strings;
import org.neo4j.kernel.api.query.QuerySnapshot;

class QueryLogFormatter
{
static void formatPageDetails( StringBuilder result, QuerySnapshot query )
{
result.append( query.pageHits() ).append( " page hits, " );
result.append( query.pageFaults() ).append( " page faults - " );
}

static void formatAllocatedBytes( StringBuilder result, QuerySnapshot query )
{
Long bytes = query.allocatedBytes();
if ( bytes != null )
{
result.append( bytes ).append( " B - " );
}
}

static void formatDetailedTime( StringBuilder result, QuerySnapshot query )
{
result.append( "(planning: " ).append( query.planningTimeMillis() );
Long cpuTime = query.cpuTimeMillis();
if ( cpuTime != null )
{
result.append( ", cpu: " ).append( cpuTime );
}
result.append( ", waiting: " ).append( query.waitTimeMillis() );
result.append( ") - " );
}

static void formatMap( StringBuilder result, Map<String,Object> params )
{
formatMap( result, params, Collections.emptySet() );
}

static void formatMap( StringBuilder result, Map<String,Object> params, Collection<String> obfuscate )
{
result.append( '{' );
if ( params != null )
{
String sep = "";
for ( Map.Entry<String,Object> entry : params.entrySet() )
{
result
.append( sep )
.append( entry.getKey() )
.append( ": " );

if ( obfuscate.contains( entry.getKey() ) )
{
result.append( "******" );
}
else
{
formatValue( result, entry.getValue() );
}
sep = ", ";
}
}
result.append( "}" );
}

private static void formatValue( StringBuilder result, Object value )
{
if ( value instanceof Map<?,?> )
{
formatMap( result, (Map<String,Object>) value );
}
else if ( value instanceof String )
{
result.append( '\'' ).append( value ).append( '\'' );
}
else
{
result.append( Strings.prettyPrint( value ) );
}
}
}
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2002-2017 "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 Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.query;

import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.neo4j.kernel.api.query.ExecutingQuery;
import org.neo4j.kernel.api.query.QuerySnapshot;
import org.neo4j.logging.Log;

class QueryLogger implements QueryExecutionMonitor
{
private final Log log;
private final long thresholdMillis;
private final boolean logQueryParameters, logDetailedTime, logAllocatedBytes, logPageDetails;

private static final Pattern PASSWORD_PATTERN = Pattern.compile(
// call signature
"(?:(?i)call)\\s+dbms(?:\\.security)?\\.change(?:User)?Password\\(" +
// optional username parameter, in single, double quotes, or parametrized
"(?:\\s*(?:'(?:(?<=\\\\)'|[^'])*'|\"(?:(?<=\\\\)\"|[^\"])*\"|[^,]*)\\s*,)?" +
// password parameter, in single, double quotes, or parametrized
"\\s*('(?:(?<=\\\\)'|[^'])*'|\"(?:(?<=\\\\)\"|[^\"])*\"|\\$\\w*|\\{\\w*\\})\\s*\\)" );

QueryLogger( Log log, long thresholdMillis, EnumSet<QueryLogEntryContent> flags )
{
this.log = log;
this.thresholdMillis = thresholdMillis;
this.logQueryParameters = flags.contains( QueryLogEntryContent.LOG_PARAMETERS );
this.logDetailedTime = flags.contains( QueryLogEntryContent.LOG_DETAILED_TIME );
this.logAllocatedBytes = flags.contains( QueryLogEntryContent.LOG_ALLOCATED_BYTES );
this.logPageDetails = flags.contains( QueryLogEntryContent.LOG_PAGE_DETAILS );
}

@Override
public void startQueryExecution( ExecutingQuery query )
{
}

@Override
public void endFailure( ExecutingQuery query, Throwable failure )
{
log.error( logEntry( query.snapshot() ), failure );
}

@Override
public void endSuccess( ExecutingQuery query )
{
QuerySnapshot snapshot = query.snapshot();
if ( snapshot.elapsedTimeMillis() >= thresholdMillis )
{
log.info( logEntry( snapshot ) );
}
}

private String logEntry( QuerySnapshot query )
{
String sourceString = query.clientConnection().asConnectionDetails();
String queryText = query.queryText();

Set<String> passwordParams = new HashSet<>();
Matcher matcher = PASSWORD_PATTERN.matcher( queryText );

while ( matcher.find() )
{
String password = matcher.group( 1 ).trim();
if ( password.charAt( 0 ) == '$' )
{
passwordParams.add( password.substring( 1 ) );
}
else if ( password.charAt( 0 ) == '{' )
{
passwordParams.add( password.substring( 1, password.length() - 1 ) );
}
else
{
queryText = queryText.replace( password, "******" );
password = "";
}
}

StringBuilder result = new StringBuilder();
result.append( query.elapsedTimeMillis() ).append( " ms: " );
if ( logDetailedTime )
{
QueryLogFormatter.formatDetailedTime( result, query );
}
if ( logAllocatedBytes )
{
QueryLogFormatter.formatAllocatedBytes( result, query );
}
if ( logPageDetails )
{
QueryLogFormatter.formatPageDetails( result, query );
}
result.append( sourceString ).append( " - " ).append( queryText );
if ( logQueryParameters )
{
QueryLogFormatter.formatMap( result.append(" - "), query.queryParameters(), passwordParams );
}
QueryLogFormatter.formatMap( result.append(" - "), query.transactionAnnotationData() );
return result.toString();
}
}

0 comments on commit 4c83502

Please sign in to comment.