Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split QueryLogger into its own source file
- Loading branch information
Showing
5 changed files
with
281 additions
and
196 deletions.
There are no files selected for viewing
43 changes: 43 additions & 0 deletions
43
enterprise/query-logging/src/main/java/org/neo4j/kernel/impl/query/QueryLogEntryContent.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ); | ||
} | ||
} |
105 changes: 105 additions & 0 deletions
105
enterprise/query-logging/src/main/java/org/neo4j/kernel/impl/query/QueryLogFormatter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ) ); | ||
} | ||
} | ||
} |
125 changes: 125 additions & 0 deletions
125
enterprise/query-logging/src/main/java/org/neo4j/kernel/impl/query/QueryLogger.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
} |
Oops, something went wrong.