001/*
002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *      Delbosc Benoit
018 */
019package org.nuxeo.common.logging;
020
021import org.apache.commons.logging.Log;
022import org.apache.commons.logging.LogFactory;
023
024/**
025 * Helper to log information that can be displayed using plantuml to render sequence UML diagram.
026 *
027 * @since 8.1
028 */
029public class SequenceTracer {
030
031    private static final Log log = LogFactory.getLog(SequenceTracer.class);
032    private static final String PREFIX = "@@ ";
033    private static final String DEFAULT_COLOR = "#white";
034    private static final int MESSAGE_MAX_LEN = 250;
035
036    // Utility class.
037    private SequenceTracer() {
038    }
039
040    /**
041     * Mark an event.
042     */
043    public static void mark(String message) {
044        if (!log.isDebugEnabled()) {
045            return;
046        }
047        final String tn = getThreadName();
048        log.debug(PREFIX + tn + " -> " + tn + ": " + message);
049    }
050
051    /**
052     * Mark the beginning of an action
053     */
054    public static void start(String message) {
055        start(message, DEFAULT_COLOR);
056    }
057
058    /**
059     * Mark the beginning of an action
060     */
061    public static void start(String message, String color) {
062        if (!log.isDebugEnabled()) {
063            return;
064        }
065        final String tn = getThreadName();
066        log.debug(PREFIX + tn + " -> " + tn + ": " + sanitize(message) + "\n" + PREFIX + "activate " + tn + " " +
067                color);
068    }
069
070    /**
071     * Mark the beginning of an action initiated by the caller.
072     */
073    public static void startFrom(final String callerThread, final String message) {
074        startFrom(callerThread, message, DEFAULT_COLOR);
075    }
076
077    /**
078     * Mark the beginning of an action initiated by the caller.
079     */
080    public static void startFrom(final String callerThread, final String message, final String color) {
081        if (!log.isDebugEnabled()) {
082            return;
083        }
084        final String tn = getThreadName();
085        log.debug(PREFIX + callerThread + " o--> " + tn + ": Initiate\n" + PREFIX
086                + tn + " -> " + tn + ": " + sanitize(message) + "\n" + PREFIX
087                + "activate " + tn + " " + color);
088    }
089
090    private static String sanitize(String message) {
091        String ret = message.replace(", ", ",\\n").replace("-", "_");
092        ret = insertNewLine(ret);
093        if (ret.length() > MESSAGE_MAX_LEN) {
094            ret = ret.substring(0, MESSAGE_MAX_LEN) + "...";
095        }
096        return ret;
097    }
098
099    private static String insertNewLine(String message) {
100        return String.join("\\n", message.split("(?<=\\G.{40})"));
101    }
102
103    /**
104     * Mark the end of the previous action.
105     */
106    public static void stop(String message) {
107        if (!log.isDebugEnabled()) {
108            return;
109        }
110        final String tn = getThreadName();
111        log.debug(PREFIX + tn + " -> " + tn + ": " + sanitize(message) + "\n" + PREFIX + "deactivate " + tn);
112    }
113
114    /**
115     * Mark the last action as failure
116     */
117    public static void destroy(String message) {
118        if (!log.isDebugEnabled()) {
119            return;
120        }
121        final String tn = getThreadName();
122        log.debug(PREFIX + tn + " -> " + tn + ": " + sanitize(message) + "\n" + PREFIX + "destroy " + tn);
123    }
124
125    /**
126     * Add a note on the current thread
127     */
128    public static void addNote(String message) {
129        if (!log.isDebugEnabled()) {
130            return;
131        }
132        log.debug(PREFIX + "note right of " + getThreadName() + ": " + message);
133    }
134
135    /**
136     * Link from source to current thread.
137     */
138    public static void addRelation(String source, String message) {
139        if (!log.isDebugEnabled()) {
140            return;
141        }
142        log.debug(PREFIX + source + " --> " + getThreadName() + ": " + sanitize(message));
143    }
144
145    /**
146     * Get the thread name sanitized for plantuml
147     */
148    public static String getThreadName() {
149        return sanitize(Thread.currentThread().getName());
150    }
151
152    public static boolean isEnabled() {
153        return log.isDebugEnabled();
154    }
155}