All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.phoenix.trace.TraceReader Maven / Gradle / Ivy

There is a newer version: 5.1.0-HBase-2.0.0.2
Show newest version
/**
 * 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.phoenix.trace;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.htrace.Span;
import org.apache.htrace.Trace;
import org.apache.phoenix.jdbc.PhoenixConnection;
import org.apache.phoenix.metrics.MetricInfo;
import org.apache.phoenix.query.QueryServices;
import org.apache.phoenix.query.QueryServicesOptions;
import org.apache.phoenix.util.LogUtil;

import com.google.common.base.Joiner;
import com.google.common.primitives.Longs;

/**
 * Read the traces written to phoenix tables by the {@link TraceWriter}.
 */
public class TraceReader {

    private static final Log LOG = LogFactory.getLog(TraceReader.class);
    private final Joiner comma = Joiner.on(',');
    private String knownColumns;
    {
        // the order here dictates the order we pull out the values below. For now, just keep them
        // in sync - so we can be efficient pulling them off the results.
        knownColumns =
                comma.join(MetricInfo.TRACE.columnName, MetricInfo.PARENT.columnName,
                    MetricInfo.SPAN.columnName, MetricInfo.DESCRIPTION.columnName,
                    MetricInfo.START.columnName, MetricInfo.END.columnName,
                    MetricInfo.HOSTNAME.columnName, TraceWriter.TAG_COUNT,
                    TraceWriter.ANNOTATION_COUNT);
    }

    private Connection conn;
    private String table;
    private int pageSize;

    public TraceReader(Connection conn, String tracingTableName) throws SQLException {
        this.conn = conn;
        this.table = tracingTableName;
        String ps = conn.getClientInfo(QueryServices.TRACING_PAGE_SIZE_ATTRIB);
        this.pageSize = ps == null ? QueryServicesOptions.DEFAULT_TRACING_PAGE_SIZE : Integer.parseInt(ps);
    }

    /**
     * Read all the currently stored traces.
     * 

* Be Careful! This could cause an OOME if there are a lot of traces. * @param limit max number of traces to return. If -1, returns all known traces. * @return the found traces * @throws SQLException */ public Collection readAll(int limit) throws SQLException { Set traces = new HashSet(); // read all the known columns from the table, sorting first by trace column (so the same // trace // goes together), and then by start time (so parent spans always appear before child spans) String query = "SELECT " + knownColumns + " FROM " + table + " ORDER BY " + MetricInfo.TRACE.columnName + " DESC, " + MetricInfo.START.columnName + " ASC" + " LIMIT " + pageSize; int resultCount = 0; ResultSet results = conn.prepareStatement(query).executeQuery(); TraceHolder trace = null; // the spans that are not the root span, but haven't seen their parent yet List orphans = null; while (results.next()) { int index = 1; long traceid = results.getLong(index++); long parent = results.getLong(index++); long span = results.getLong(index++); String desc = results.getString(index++); long start = results.getLong(index++); long end = results.getLong(index++); String host = results.getString(index++); int tagCount = results.getInt(index++); int annotationCount = results.getInt(index++); // we have a new trace if (trace == null || traceid != trace.traceid) { // only increment if we are on a new trace, to ensure we get at least one if (trace != null) { resultCount++; } // we beyond the limit, so we stop if (resultCount >= limit) { break; } trace = new TraceHolder(); // add the orphans, so we can track them later orphans = new ArrayList(); trace.orphans = orphans; trace.traceid = traceid; traces.add(trace); } // search the spans to determine the if we have a known parent SpanInfo parentSpan = null; if (parent != Span.ROOT_SPAN_ID) { // find the parent for (SpanInfo p : trace.spans) { if (p.id == parent) { parentSpan = p; break; } } } SpanInfo spanInfo = new SpanInfo(parentSpan, parent, span, desc, start, end, host, tagCount, annotationCount); // search the orphans to see if this is the parent id for (int i = 0; i < orphans.size(); i++) { SpanInfo orphan = orphans.get(i); // we found the parent for the orphan if (orphan.parentId == span) { // update the bi-directional relationship orphan.parent = spanInfo; spanInfo.children.add(orphan); // / its no longer an orphan LOG.trace(addCustomAnnotations("Found parent for span: " + span)); orphans.remove(i--); } } if (parentSpan != null) { // add this as a child to the parent span parentSpan.children.add(spanInfo); } else if (parent != Span.ROOT_SPAN_ID) { // add the span to the orphan pile to check for the remaining spans we see LOG.info(addCustomAnnotations("No parent span found for span: " + span + " (root span id: " + Span.ROOT_SPAN_ID + ")")); orphans.add(spanInfo); } // add the span to the full known list trace.spans.add(spanInfo); // go back and find the tags for the row spanInfo.tags.addAll(getTags(traceid, parent, span, tagCount)); spanInfo.annotations.addAll(getAnnotations(traceid, parent, span, annotationCount)); } // make sure we clean up after ourselves results.close(); return traces; } private Collection getTags(long traceid, long parent, long span, int count) throws SQLException { return getDynamicCountColumns(traceid, parent, span, count, TraceWriter.TAG_FAMILY, MetricInfo.TAG.columnName); } private Collection getAnnotations(long traceid, long parent, long span, int count) throws SQLException { return getDynamicCountColumns(traceid, parent, span, count, TraceWriter.ANNOTATION_FAMILY, MetricInfo.ANNOTATION.columnName); } private Collection getDynamicCountColumns(long traceid, long parent, long span, int count, String family, String columnName) throws SQLException { if (count == 0) { return Collections.emptyList(); } // build the column strings, family.column String[] parts = new String[count]; for (int i = 0; i < count; i++) { parts[i] = TraceWriter.getDynamicColumnName(family, columnName, i); } // join the columns together String columns = comma.join(parts); // redo them and add "VARCHAR to the end, so we can specify the columns for (int i = 0; i < count; i++) { parts[i] = parts[i] + " VARCHAR"; } String dynamicColumns = comma.join(parts); String request = "SELECT " + columns + " from " + table + "(" + dynamicColumns + ") WHERE " + MetricInfo.TRACE.columnName + "=" + traceid + " AND " + MetricInfo.PARENT.columnName + "=" + parent + " AND " + MetricInfo.SPAN.columnName + "=" + span; LOG.trace(addCustomAnnotations("Requesting columns with: " + request)); ResultSet results = conn.createStatement().executeQuery(request); List cols = new ArrayList(); while (results.next()) { for (int index = 1; index <= count; index++) { cols.add(results.getString(index)); } } if (cols.size() < count) { LOG.error(addCustomAnnotations("Missing tags! Expected " + count + ", but only got " + cols.size() + " tags from rquest " + request)); } return cols; } private String addCustomAnnotations(String logLine) throws SQLException { if (conn.isWrapperFor(PhoenixConnection.class)) { PhoenixConnection phxConn = conn.unwrap(PhoenixConnection.class); logLine = LogUtil.addCustomAnnotations(logLine, phxConn); } return logLine; } /** * Holds information about a trace */ public static class TraceHolder { public List orphans; public long traceid; public TreeSet spans = new TreeSet(); @Override public int hashCode() { return new Long(traceid).hashCode(); } @Override public boolean equals(Object o) { if (o instanceof TraceHolder) { return traceid == ((TraceHolder) o).traceid; } return false; } @Override public String toString() { StringBuilder sb = new StringBuilder("Trace: " + traceid + "\n"); // get the first span, which is always going to be the root span SpanInfo root = spans.iterator().next(); if (root.parent != null) { sb.append("Root span not present! Just printing found spans\n"); for (SpanInfo span : spans) { sb.append(span.toString() + "\n"); } } else { // print the tree of spans List toPrint = new ArrayList(); toPrint.add(root); while (!toPrint.isEmpty()) { SpanInfo span = toPrint.remove(0); sb.append(span.toString() + "\n"); toPrint.addAll(span.children); } } if (orphans.size() > 0) { sb.append("Found orphan spans:\n" + orphans); } return sb.toString(); } } public static class SpanInfo implements Comparable { public SpanInfo parent; public List children = new ArrayList(); public String description; public long id; public long start; public long end; public String hostname; public int tagCount; public List tags = new ArrayList(); public int annotationCount; public List annotations = new ArrayList(); private long parentId; public SpanInfo(SpanInfo parent, long parentid, long span, String desc, long start, long end, String host, int tagCount, int annotationCount) { this.parent = parent; this.parentId = parentid; this.id = span; this.description = desc; this.start = start; this.end = end; this.hostname = host; this.tagCount = tagCount; this.annotationCount = annotationCount; } @Override public int hashCode() { return new Long(id).hashCode(); } @Override public boolean equals(Object o) { if (o instanceof SpanInfo) { return id == ((SpanInfo) o).id; } return false; } /** * Do the same sorting that we would get from reading the table with a {@link TraceReader}, * specifically, by trace and then by start/end. However, these are only every stored in a * single trace, so we can just sort on start/end times. */ @Override public int compareTo(SpanInfo o) { // root span always comes first if (this.parentId == Span.ROOT_SPAN_ID) { return -1; } else if (o.parentId == Span.ROOT_SPAN_ID) { return 1; } int compare = Longs.compare(start, o.start); if (compare == 0) { compare = Longs.compare(end, o.end); if (compare == 0) { return Longs.compare(id, o.id); } } return compare; } @Override public String toString() { StringBuilder sb = new StringBuilder("Span: " + id + "\n"); sb.append("\tdescription=" + description); sb.append("\n"); sb.append("\tparent=" + (parent == null ? (parentId == Span.ROOT_SPAN_ID ? "ROOT" : "[orphan - id: " + parentId + "]") : parent.id)); sb.append("\n"); sb.append("\tstart,end=" + start + "," + end); sb.append("\n"); sb.append("\telapsed=" + (end - start)); sb.append("\n"); sb.append("\thostname=" + hostname); sb.append("\n"); sb.append("\ttags=(" + tagCount + ") " + tags); sb.append("\n"); sb.append("\tannotations=(" + annotationCount + ") " + annotations); sb.append("\n"); sb.append("\tchildren="); for (SpanInfo child : children) { sb.append(child.id + ", "); } sb.append("\n"); return sb.toString(); } public long getParentIdForTesting() { return parentId; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy