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

com.unboundid.ldap.sdk.unboundidds.logs.LogMessage Maven / Gradle / Ivy

/*
 * Copyright 2009-2024 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2009-2024 Ping Identity Corporation
 *
 * Licensed 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.
 */
/*
 * Copyright (C) 2009-2024 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.ldap.sdk.unboundidds.logs;



import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.Map;

import com.unboundid.util.ByteStringBuffer;
import com.unboundid.util.Debug;
import com.unboundid.util.NotExtensible;
import com.unboundid.util.NotMutable;
import com.unboundid.util.NotNull;
import com.unboundid.util.Nullable;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;

import static com.unboundid.ldap.sdk.unboundidds.logs.LogMessages.*;



/**
 * This class provides a data structure that holds information about a log
 * message contained in a Directory Server access or error log file.
 * 
*
* NOTE: This class, and other classes within the * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only * supported for use against Ping Identity, UnboundID, and * Nokia/Alcatel-Lucent 8661 server products. These classes provide support * for proprietary functionality or for external specifications that are not * considered stable or mature enough to be guaranteed to work in an * interoperable way with other types of LDAP servers. *
*/ @NotExtensible() @NotMutable() @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) public class LogMessage implements Serializable { /** * The format string that will be used for log message timestamps * with seconds-level precision enabled. */ @NotNull private static final String TIMESTAMP_SEC_FORMAT = "'['dd/MMM/yyyy:HH:mm:ss Z']'"; /** * The format string that will be used for log message timestamps * with seconds-level precision enabled. */ @NotNull private static final String TIMESTAMP_MS_FORMAT = "'['dd/MMM/yyyy:HH:mm:ss.SSS Z']'"; /** * The thread-local date formatter. */ @NotNull private static final ThreadLocal dateSecFormat = new ThreadLocal<>(); /** * The thread-local date formatter. */ @NotNull private static final ThreadLocal dateMsFormat = new ThreadLocal<>(); /** * The serial version UID for this serializable class. */ private static final long serialVersionUID = -1210050773534504972L; // The timestamp for this log message. @NotNull private final Date timestamp; // The map of named fields contained in this log message. @NotNull private final Map namedValues; // The set of unnamed values contained in this log message. @NotNull private final Set unnamedValues; // The string representation of this log message. @NotNull private final String messageString; /** * Creates a log message from the provided log message. * * @param m The log message to use to create this log message. */ protected LogMessage(@NotNull final LogMessage m) { timestamp = m.timestamp; unnamedValues = m.unnamedValues; namedValues = m.namedValues; messageString = m.messageString; } /** * Parses the provided string as a log message. * * @param s The string to be parsed as a log message. * * @throws LogException If the provided string cannot be parsed as a valid * log message. */ protected LogMessage(@NotNull final String s) throws LogException { messageString = s; // The first element should be the timestamp, which should end with a // closing bracket. final int bracketPos = s.indexOf(']'); if (bracketPos < 0) { throw new LogException(s, ERR_LOG_MESSAGE_NO_TIMESTAMP.get()); } final String timestampString = s.substring(0, bracketPos+1); SimpleDateFormat f; if (timestampIncludesMilliseconds(timestampString)) { f = dateMsFormat.get(); if (f == null) { f = new SimpleDateFormat(TIMESTAMP_MS_FORMAT); f.setLenient(false); dateMsFormat.set(f); } } else { f = dateSecFormat.get(); if (f == null) { f = new SimpleDateFormat(TIMESTAMP_SEC_FORMAT); f.setLenient(false); dateSecFormat.set(f); } } try { timestamp = f.parse(timestampString); } catch (final Exception e) { Debug.debugException(e); throw new LogException(s, ERR_LOG_MESSAGE_INVALID_TIMESTAMP.get( StaticUtils.getExceptionMessage(e)), e); } // The remainder of the message should consist of named and unnamed values. final LinkedHashMap named = new LinkedHashMap<>(StaticUtils.computeMapCapacity(10)); final LinkedHashSet unnamed = new LinkedHashSet<>(StaticUtils.computeMapCapacity(10)); parseTokens(s, bracketPos+1, named, unnamed); namedValues = Collections.unmodifiableMap(named); unnamedValues = Collections.unmodifiableSet(unnamed); } /** * Parses the set of named and unnamed tokens from the provided message * string. * * @param s The complete message string being parsed. * @param startPos The position at which to start parsing. * @param named The map in which to place the named tokens. * @param unnamed The set in which to place the unnamed tokens. * * @throws LogException If a problem occurs while processing the tokens. */ private static void parseTokens(@NotNull final String s, final int startPos, @NotNull final Map named, @NotNull final Set unnamed) throws LogException { boolean inQuotes = false; final StringBuilder buffer = new StringBuilder(); for (int p=startPos; p < s.length(); p++) { final char c = s.charAt(p); if ((c == ' ') && (! inQuotes)) { if (buffer.length() > 0) { processToken(s, buffer.toString(), named, unnamed); buffer.delete(0, buffer.length()); } } else if (c == '"') { inQuotes = (! inQuotes); } else { buffer.append(c); } } if (buffer.length() > 0) { processToken(s, buffer.toString(), named, unnamed); } } /** * Processes the provided token and adds it to the appropriate collection. * * @param s The complete message string being parsed. * @param token The token to be processed. * @param named The map in which to place named tokens. * @param unnamed The set in which to place unnamed tokens. * * @throws LogException If a problem occurs while processing the token. */ private static void processToken(@NotNull final String s, @NotNull final String token, @NotNull final Map named, @NotNull final Set unnamed) throws LogException { // If the token contains an equal sign, then it's a named token. Otherwise, // it's unnamed. final int equalPos = token.indexOf('='); if (equalPos < 0) { // Unnamed tokens should never need any additional processing. unnamed.add(token); } else { // The name of named tokens should never need any additional processing. // The value may need to be processed to remove surrounding quotes and/or // to un-escape any special characters. final String name = token.substring(0, equalPos); final String value = processValue(s, token.substring(equalPos+1)); named.put(name, value); } } /** * Performs any processing needed on the provided value to obtain the original * text. This may include removing surrounding quotes and/or un-escaping any * special characters. * * @param s The complete message string being parsed. * @param v The value to be processed. * * @return The processed version of the provided string. * * @throws LogException If a problem occurs while processing the value. */ @NotNull() private static String processValue(@NotNull final String s, @NotNull final String v) throws LogException { final ByteStringBuffer b = new ByteStringBuffer(); for (int i=0; i < v.length(); i++) { final char c = v.charAt(i); if (c == '"') { // This should only happen at the beginning or end of the string, in // which case it should be stripped out so we don't need to do anything. } else if (c == '#') { // Every octothorpe should be followed by exactly two hex digits, which // represent a byte of a UTF-8 character. if (i > (v.length() - 3)) { throw new LogException(s, ERR_LOG_MESSAGE_INVALID_ESCAPED_CHARACTER.get(v)); } byte rawByte = 0x00; for (int j=0; j < 2; j++) { rawByte <<= 4; switch (v.charAt(++i)) { case '0': break; case '1': rawByte |= 0x01; break; case '2': rawByte |= 0x02; break; case '3': rawByte |= 0x03; break; case '4': rawByte |= 0x04; break; case '5': rawByte |= 0x05; break; case '6': rawByte |= 0x06; break; case '7': rawByte |= 0x07; break; case '8': rawByte |= 0x08; break; case '9': rawByte |= 0x09; break; case 'a': case 'A': rawByte |= 0x0A; break; case 'b': case 'B': rawByte |= 0x0B; break; case 'c': case 'C': rawByte |= 0x0C; break; case 'd': case 'D': rawByte |= 0x0D; break; case 'e': case 'E': rawByte |= 0x0E; break; case 'f': case 'F': rawByte |= 0x0F; break; default: throw new LogException(s, ERR_LOG_MESSAGE_INVALID_ESCAPED_CHARACTER.get(v)); } } b.append(rawByte); } else { b.append(c); } } return b.toString(); } /** * Determines whether a string that represents a timestamp includes a * millisecond component. * * @param timestamp The timestamp string to examine. * * @return {@code true} if the given string includes a millisecond component, * or {@code false} if not. */ private static boolean timestampIncludesMilliseconds( @NotNull final String timestamp) { // The sec and ms format strings differ at the 22nd character. return ((timestamp.length() > 21) && (timestamp.charAt(21) == '.')); } /** * Retrieves the timestamp for this log message. * * @return The timestamp for this log message. */ @NotNull() public final Date getTimestamp() { return timestamp; } /** * Retrieves the set of named tokens for this log message, mapped from the * name to the corresponding value. * * @return The set of named tokens for this log message. */ @NotNull() public final Map getNamedValues() { return namedValues; } /** * Retrieves the value of the token with the specified name. * * @param name The name of the token to retrieve. * * @return The value of the token with the specified name, or {@code null} if * there is no value with the specified name. */ @Nullable() public final String getNamedValue(@NotNull final String name) { return namedValues.get(name); } /** * Retrieves the value of the token with the specified name as a * {@code Boolean}. * * @param name The name of the token to retrieve. * * @return The value of the token with the specified name as a * {@code Boolean}, or {@code null} if there is no value with the * specified name or the value cannot be parsed as a {@code Boolean}. */ @Nullable() public final Boolean getNamedValueAsBoolean(@NotNull final String name) { final String s = namedValues.get(name); if (s == null) { return null; } final String lowerValue = StaticUtils.toLowerCase(s); if (lowerValue.equals("true") || lowerValue.equals("t") || lowerValue.equals("yes") || lowerValue.equals("y") || lowerValue.equals("on") || lowerValue.equals("1")) { return Boolean.TRUE; } else if (lowerValue.equals("false") || lowerValue.equals("f") || lowerValue.equals("no") || lowerValue.equals("n") || lowerValue.equals("off") || lowerValue.equals("0")) { return Boolean.FALSE; } else { return null; } } /** * Retrieves the value of the token with the specified name as a * {@code Double}. * * @param name The name of the token to retrieve. * * @return The value of the token with the specified name as a * {@code Double}, or {@code null} if there is no value with the * specified name or the value cannot be parsed as a {@code Double}. */ @Nullable() public final Double getNamedValueAsDouble(@NotNull final String name) { final String s = namedValues.get(name); if (s == null) { return null; } try { return Double.valueOf(s); } catch (final Exception e) { Debug.debugException(e); return null; } } /** * Retrieves the value of the token with the specified name as an * {@code Integer}. * * @param name The name of the token to retrieve. * * @return The value of the token with the specified name as an * {@code Integer}, or {@code null} if there is no value with the * specified name or the value cannot be parsed as an * {@code Integer}. */ @Nullable() public final Integer getNamedValueAsInteger(@NotNull final String name) { final String s = namedValues.get(name); if (s == null) { return null; } try { return Integer.valueOf(s); } catch (final Exception e) { Debug.debugException(e); return null; } } /** * Retrieves the value of the token with the specified name as a {@code Long}. * * @param name The name of the token to retrieve. * * @return The value of the token with the specified name as a {@code Long}, * or {@code null} if there is no value with the specified name or * the value cannot be parsed as a {@code Long}. */ @Nullable() public final Long getNamedValueAsLong(@NotNull final String name) { final String s = namedValues.get(name); if (s == null) { return null; } try { return Long.valueOf(s); } catch (final Exception e) { Debug.debugException(e); return null; } } /** * Retrieves the set of unnamed tokens for this log message. * * @return The set of unnamed tokens for this log message. */ @NotNull() public final Set getUnnamedValues() { return unnamedValues; } /** * Indicates whether this log message has the specified unnamed value. * * @param value The value for which to make the determination. * * @return {@code true} if this log message has the specified unnamed value, * or {@code false} if not. */ public final boolean hasUnnamedValue(@NotNull final String value) { return unnamedValues.contains(value); } /** * Retrieves a string representation of this log message. * * @return A string representation of this log message. */ @Override() @NotNull() public final String toString() { return messageString; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy