/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.cxf.tracing.htrace.ext; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.StringUtils; import org.apache.htrace.core.HTraceConfiguration; import org.apache.htrace.core.Span; import org.apache.htrace.core.SpanReceiver; import org.apache.htrace.core.TimelineAnnotation; /** * Span receiver implementation which outputs spans into logs. */ public class LoggingSpanReceiver extends SpanReceiver { public static final String LOG_LEVEL_KEY = "cxf.log.level"; public static final String LOG_LEVEL_ERROR = Level.SEVERE.getName(); public static final String LOG_LEVEL_DEBUG = Level.FINE.getName(); public static final String LOG_LEVEL_INFO = Level.INFO.getName(); public static final String LOG_LEVEL_WARN = Level.WARNING.getName(); private interface Stringifier<T> { String toString(T value); } private static final Logger LOG = LogUtils.getL7dLogger(LoggingSpanReceiver.class); private final Level level; private final Stringifier<TimelineAnnotation> timelineStringifier = new Stringifier<TimelineAnnotation>() { @Override public String toString(TimelineAnnotation annotation) { final StringBuilder sb = new StringBuilder(); append(sb, "time", annotation.getTime()); append(sb, "message", annotation.getMessage(), true); return "[" + sb.toString() + "]"; } }; public LoggingSpanReceiver(HTraceConfiguration conf) { level = Level.parse(conf.get(LOG_LEVEL_KEY, LOG_LEVEL_DEBUG)); } @Override public void receiveSpan(Span span) { LOG.log(level, toString(span)); } @Override public void close() throws IOException { } /** * Sample log statements: * * INFO org.apache.cxf.tracing.htrace.ext.LoggingSpanReceiver - spanId=e5999a29a1ea468201acac30ec04ae39 * tracerId="tracer-server/192.168.0.100" start=1488049449621 stop=1488049451623 description="Get Employees" * parents=[e5999a29a1ea4682d346ae17e51e0bd4] kvs=[] timelines=[[time=1488049451623 * message="Getting all employees"]] * * INFO org.apache.cxf.tracing.htrace.ext.LoggingSpanReceiver - spanId=e5999a29a1ea4682ac0a9ad638e084ed * tracerId="tracer-client/192.168.0.100" start=1488049449074 stop=1488049454894 * description="GET http://localhost:8282/rest/api/people" parents=[] kvs=[] timelines=[] */ private String toString(Span span) { final StringBuilder sb = new StringBuilder(); if (span.getSpanId().isValid()) { append(sb, "spanId", span.getSpanId().toString()); } String tracerId = span.getTracerId(); if (!StringUtils.isEmpty(tracerId)) { append(sb, "tracerId", tracerId, true); } if (span.getStartTimeMillis() != 0) { append(sb, "start", span.getStartTimeMillis()); } if (span.getStopTimeMillis() != 0) { append(sb, "stop", span.getStopTimeMillis()); } if (!StringUtils.isEmpty(span.getDescription())) { append(sb, "description", span.getDescription(), true); } append(sb, "parents", span.getParents()); append(sb, "kvs", span.getKVAnnotations()); append(sb, "timelines", span.getTimelineAnnotations(), timelineStringifier); return sb.toString(); } private void append(StringBuilder sb, String key, Map<String, String> values) { final StringBuilder inner = new StringBuilder(); for (final Map.Entry<String, String> entry : values.entrySet()) { append(inner, quote(entry.getKey()), entry.getValue(), true); } append(sb, key, inner.insert(0, "[").append("]").toString()); } private<T> void append(StringBuilder sb, String key, Collection<T> values, Stringifier<T> stringifier) { append(sb, key, toString(values, stringifier)); } private<T> void append(StringBuilder sb, String key, T[] values) { append(sb, key, toString(values)); } private void append(StringBuilder sb, String key, long value) { append(sb, key, Long.toString(value)); } private void append(StringBuilder sb, String key, String value) { append(sb, key, value, false); } private void append(StringBuilder sb, String key, String value, boolean quoted) { if (sb.length() > 0) { sb.append(" "); } sb.append(key).append("="); quote(sb, value, quoted); } private String quote(String value) { final StringBuilder sb = new StringBuilder(); quote(sb, value, true); return sb.toString(); } private<T> String toString(Collection<T> values, Stringifier<T> stringifier) { final Collection<String> converted = new ArrayList<>(); for (final T value: values) { converted.add(stringifier.toString(value)); } return Arrays.toString(converted.toArray()); } private<T> String toString(T[] values) { final Collection<String> converted = new ArrayList<>(); for (final T value: values) { converted.add(value.toString()); } return Arrays.toString(converted.toArray()); } private void quote(StringBuilder sb, String value, boolean quoted) { if (quoted) { sb.append("\""); } sb.append(value); if (quoted) { sb.append("\""); } } }