You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by "Paulo Motta (JIRA)" <ji...@apache.org> on 2016/03/09 20:18:41 UTC
[jira] [Commented] (CASSANDRA-7276) Include keyspace and table
names in logs where possible
[ https://issues.apache.org/jira/browse/CASSANDRA-7276?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15187713#comment-15187713 ]
Paulo Motta commented on CASSANDRA-7276:
----------------------------------------
Thanks and sorry for the delay.
I'm a bit worried that the repetition of log statements with the \[ks.cf\] prefix is error prone and cumbersome so may not be followed by other developers, so I think we can improve this by providing a {{ContextualizedLogger}} that will wrap a {{Logger}} object and provide utillity methods to automatically add the \[ks.cf\] prefix. This will allow us to change the prefix format if necessary and easily add new contextualized log statements types in the future.
I thought of something along those lines (these are just prototypes/suggestions):
{noformat}
public class ContextualizedLogger implements Logger
{
private final Logger logger;
public static ContextualizedLogger getLogger(Class clazz) {
return new ContextualizedLogger(LoggerFactory.getLogger(clazz));
}
private ContextualizedLogger(Logger logger)
{
this.logger = logger;
}
public void debug(ColumnFamilyStore cfs, String msg, Object... args)
{
debug(cfs.name, cfs.keyspace.getName(), msg, args);
}
public void debug(CFMetaData cfm, String msg, Object... args)
{
debug(cfm.ksName, cfm.cfName, msg, args);
}
public void debug(String ks, String table, String msg, Object... args)
{
logger.debug(extendMsg(msg), extendArgs(ks, table, args));
}
private String extendMsg(String msg)
{
return String.format("[{}.{}] %s", msg);
}
private Object[] extendArgs(String ks, String table, Object[] args)
{
Object[] newArgs = new Object[args.length+2];
newArgs[0] = ks;
newArgs[1] = table;
System.arraycopy(args, 0, newArgs, 2, args.length);
return newArgs;
}
public String getName()
{
return logger.getName();
}
public boolean isTraceEnabled()
{
return logger.isTraceEnabled();
}
public void trace(String s)
{
logger.trace(s);
}
public boolean isTraceEnabled(Marker marker)
{
return logger.isTraceEnabled(marker);
}
}
{noformat}
So this, could be used like this, for instance:
{noformat}
public class ColumnFamilyStore implements ColumnFamilyStoreMBean
{
private static final ContextualizedLogger logger = ContextualizedLogger.getLogger(ColumnFamilyStore.class);
private void logFlush()
{
logger.debug(this, "Enqueuing flush: {}", String.format("%d (%.0f%%) on-heap, %d (%.0f%%) off-heap", onHeapTotal,
onHeapRatio * 100, offHeapTotal, offHeapRatio * 100));
}
}
{noformat}
or
{noformat}
public class CompactionManager implements CompactionManagerMBean
{
private static final ContextualizedLogger logger = ContextualizedLogger.getLogger(CompactionManager.class);
public Future<?> submitCacheWrite(final AutoSavingCache.Writer writer)
{
Runnable runnable = new Runnable()
{
public void run()
{
if (!AutoSavingCache.flushInProgress.add(writer.cacheType()))
{
CFMetaData cfm = writer.getCompactionInfo().getCFMetaData();
logger.debug(cfm, "Cache flushing was already in progress: skipping {}", writer.getCompactionInfo());
return;
}
}
}
}
}
{noformat}
WDYT about this approach? If you agree please add support to this and use it on all log statements of {{ColumnFamilyStore}}, {{CompactionManager}}, {{Memtable}}, {{CompactionTask}} (and subclasses). You may need to update log statements that already mention ks/cf info to only show those only in the prefix, for example:
* {{Loading new SSTables and building secondary indexes for ks/cf: sstable_XYZ}} would become {{\[ks.cf\] Loading new SSTables and building secondary indexes: sstable_XYZ}}
We can also improve {{ContextualizedException}} to add the {{ks/cf}} info to the Exception message itself, so it will be automatically print when the stack trace is print by the uncaught exception handler. I modified the class to do this so you can reuse it on your next patch:
{noformat}
diff --git a/src/java/org/apache/cassandra/exceptions/ContextualizedException.java b/src/java/org/apache/cassandra/exceptions/ContextualizedException.java
index 68c3e54..9cc5a93 100644
--- a/src/java/org/apache/cassandra/exceptions/ContextualizedException.java
+++ b/src/java/org/apache/cassandra/exceptions/ContextualizedException.java
@@ -19,6 +19,9 @@ package org.apache.cassandra.exceptions;
import java.util.List;
import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
/**
* This exception is used by MessageDeliveryTask to
@@ -49,4 +52,28 @@ public class ContextualizedException extends RuntimeException {
this.keyspace = keyspace;
this.tables = tables;
}
+
+ public String getPrettyKeyspaceAndTables()
+ {
+ if (keyspace.isPresent() && !tables.isPresent())
+ {
+ return String.format("Exception on keyspace %s: ", keyspace.get());
+ }
+
+ if (keyspace.isPresent() && tables.isPresent() && !tables.get().isEmpty())
+ {
+ List<String> ksTables = tables.get().stream().map(t -> String.format("%s.%s", keyspace.get(), t)).collect(Collectors.toList());
+ if (ksTables.size() == 1)
+ return String.format("Exception on table %s: ", ksTables.get(0));
+ else
+ return String.format("Exception on tables %s: ", StringUtils.join(ksTables, ","));
+ }
+
+ return "";
+ }
+
+ public String getMessage()
+ {
+ return String.format("%s%s", getPrettyKeyspaceAndTables(), super.getMessage());
+ }
}
{noformat}
Another improvement we can make is to add a constructor to {{ContextualizedException}} that receives a keyspace, a table and a message, and replace the occurrences of {{RuntimeException}} on {{ColumnFamilyStore}} with a {{ContextualizedException}} that will automatically print the message in the correct format without the need to add a prefix to the exception message as we've been doing before.
Some other nits from the previous patch:
* Fix typo {{IContexualizedVerbHandler}} -> {{IContextualizedVerbHandler}}
* Rename {{getColumnFamilyNames}} on {{CounterMutation}} and {{Mutation}} to {{getTableNames}}.
* Use a ternary if on {{RepairMessageVerbHandler.getKeyspace}} and {{getTables}}.
* On {{Mutation.toString()}} you're printing the {{getTableNames()}} {{Optional}} but you should check if it's empty first.
> Include keyspace and table names in logs where possible
> -------------------------------------------------------
>
> Key: CASSANDRA-7276
> URL: https://issues.apache.org/jira/browse/CASSANDRA-7276
> Project: Cassandra
> Issue Type: Improvement
> Reporter: Tyler Hobbs
> Priority: Minor
> Labels: bootcamp, lhf
> Fix For: 2.1.x
>
> Attachments: 0001-Better-Logging-for-KS-and-CF.patch, 0001-Logging-KS-and-CF-consistently.patch, 0001-Logging-for-Keyspace-and-Tables.patch, 2.1-CASSANDRA-7276-v1.txt, cassandra-2.1-7276-compaction.txt, cassandra-2.1-7276.txt, cassandra-2.1.9-7276-v2.txt, cassandra-2.1.9-7276.txt
>
>
> Most error messages and stacktraces give you no clue as to what keyspace or table was causing the problem. For example:
> {noformat}
> ERROR [MutationStage:61648] 2014-05-20 12:05:45,145 CassandraDaemon.java (line 198) Exception in thread Thread[MutationStage:61648,5,main]
> java.lang.IllegalArgumentException
> at java.nio.Buffer.limit(Unknown Source)
> at org.apache.cassandra.db.marshal.AbstractCompositeType.getBytes(AbstractCompositeType.java:63)
> at org.apache.cassandra.db.marshal.AbstractCompositeType.getWithShortLength(AbstractCompositeType.java:72)
> at org.apache.cassandra.db.marshal.AbstractCompositeType.compare(AbstractCompositeType.java:98)
> at org.apache.cassandra.db.marshal.AbstractCompositeType.compare(AbstractCompositeType.java:35)
> at edu.stanford.ppl.concurrent.SnapTreeMap$1.compareTo(SnapTreeMap.java:538)
> at edu.stanford.ppl.concurrent.SnapTreeMap.attemptUpdate(SnapTreeMap.java:1108)
> at edu.stanford.ppl.concurrent.SnapTreeMap.updateUnderRoot(SnapTreeMap.java:1059)
> at edu.stanford.ppl.concurrent.SnapTreeMap.update(SnapTreeMap.java:1023)
> at edu.stanford.ppl.concurrent.SnapTreeMap.putIfAbsent(SnapTreeMap.java:985)
> at org.apache.cassandra.db.AtomicSortedColumns$Holder.addColumn(AtomicSortedColumns.java:328)
> at org.apache.cassandra.db.AtomicSortedColumns.addAllWithSizeDelta(AtomicSortedColumns.java:200)
> at org.apache.cassandra.db.Memtable.resolve(Memtable.java:226)
> at org.apache.cassandra.db.Memtable.put(Memtable.java:173)
> at org.apache.cassandra.db.ColumnFamilyStore.apply(ColumnFamilyStore.java:893)
> at org.apache.cassandra.db.Keyspace.apply(Keyspace.java:368)
> at org.apache.cassandra.db.Keyspace.apply(Keyspace.java:333)
> at org.apache.cassandra.db.RowMutation.apply(RowMutation.java:206)
> at org.apache.cassandra.db.RowMutationVerbHandler.doVerb(RowMutationVerbHandler.java:56)
> at org.apache.cassandra.net.MessageDeliveryTask.run(MessageDeliveryTask.java:60)
> at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
> at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
> at java.lang.Thread.run(Unknown Source)
> {noformat}
> We should try to include info on the keyspace and column family in the error messages or logs whenever possible. This includes reads, writes, compactions, flushes, repairs, and probably more.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)