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

com.hivemq.logging.EventLog Maven / Gradle / Ivy

There is a newer version: 2025.1
Show newest version
/*
 * Copyright 2019-present HiveMQ GmbH
 *
 * 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.
 */
package com.hivemq.logging;

import com.hivemq.bootstrap.ClientConnectionContext;
import com.hivemq.bootstrap.ioc.lazysingleton.LazySingleton;
import com.hivemq.extension.sdk.api.annotations.NotNull;
import com.hivemq.extension.sdk.api.annotations.Nullable;
import com.hivemq.mqtt.message.reason.Mqtt5AuthReasonCode;
import com.hivemq.configuration.service.entity.Listener;

import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

/**
 * The EventLog class is used to log certain events that could be important for customers to separate files.
 * In a future state of the implementation it may also be used to display those events in the web-interface.
 */
@LazySingleton
public class EventLog {

    private static final Logger log = LoggerFactory.getLogger(EventLog.class);

    public static final String EVENT_CLIENT_CONNECTED = "event.client-connected";
    public static final String EVENT_CLIENT_DISCONNECTED = "event.client-disconnected";
    public static final String EVENT_MESSAGE_DROPPED = "event.message-dropped";
    public static final String EVENT_CLIENT_SESSION_EXPIRED = "event.client-session-expired";
    public static final String EVENT_AUTHENTICATION = "event.authentication";
    /**
     * Events are logged to DEBUG, in case customers are using a custom logback.xml
     */

    private static final Logger logClientConnected = LoggerFactory.getLogger(EVENT_CLIENT_CONNECTED);
    private static final Logger logClientDisconnected = LoggerFactory.getLogger(EVENT_CLIENT_DISCONNECTED);
    private static final Logger logMessageDropped = LoggerFactory.getLogger(EVENT_MESSAGE_DROPPED);
    private static final Logger logClientSessionExpired = LoggerFactory.getLogger(EVENT_CLIENT_SESSION_EXPIRED);
    private static final Logger logAuthentication = LoggerFactory.getLogger(EVENT_AUTHENTICATION);

    private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    public static final ZoneId ZONE = ZoneId.of("UTC");

    /**
     * Log that a outgoing publish message was dropped.
     *
     * @param clientId of the subscriber that would have received the message
     * @param topic    of the publish message
     * @param qos      of the publish message
     * @param reason   why the message was dropped
     */
    public void messageDropped(
            @Nullable final String clientId,
            @Nullable final String topic,
            @NotNull final int qos,
            @NotNull final String reason) {
        logMessageDropped.debug(
                "Outgoing publish message was dropped. Receiving client: {}, topic: {}, qos: {}, reason: {}.",
                valueOrUnknown(clientId),
                valueOrUnknown(topic),
                qos,
                reason);
    }

    /**
     * Log that a outgoing publish message for a shared subscription was dropped.
     *
     * @param group  of the shared subscription
     * @param topic  of the publish message
     * @param qos    of the publish message
     * @param reason why the message was dropped
     */
    public void sharedSubscriptionMessageDropped(
            @Nullable final String group,
            @Nullable final String topic,
            @NotNull final int qos,
            @NotNull final String reason) {
        logMessageDropped.debug(
                "Outgoing publish message was dropped. Receiving shared subscription group: {}, topic: {}, qos: {}, reason: {}.",
                valueOrUnknown(group),
                valueOrUnknown(topic),
                qos,
                reason);
    }

    /**
     * Log that a outgoing MQTT message for a client was dropped.
     *
     * @param client      identifier of the client that would have received the message
     * @param messageType MQTT message type
     * @param reason      why the message was dropped
     */
    public void mqttMessageDropped(
            @Nullable final String client, @Nullable final String messageType, @NotNull final String reason) {
        logMessageDropped.debug("Outgoing MQTT packet was dropped. Receiving client: {}, messageType: {}, reason: {}.",
                valueOrUnknown(client),
                valueOrUnknown(messageType),
                reason);
    }

    /**
     * Log that a client has successfully connected to the broker.
     *
     * @param channel    of the client connection
     * @param cleanStart if the connection was started clean
     */
    public void clientConnected(final @NotNull Channel channel, final boolean cleanStart) {
        final ClientConnectionContext clientConnectionContext = ClientConnectionContext.of(channel);
        final String clientId = clientConnectionContext.getClientId();
        final String ip = clientConnectionContext.getChannelIP().orElse(null);
        final Long sessionExpiry = clientConnectionContext.getClientSessionExpiryInterval();

        logClientConnected.debug("Client ID: {}, IP: {}, Clean Start: {}, Session Expiry: {} connected.",
                valueOrUnknown(clientId),
                valueOrUnknown(ip),
                valueOrUnknown(cleanStart),
                valueOrUnknown(sessionExpiry));
    }

    /**
     * Log that the connection to a client was closed gracefully, regardless if the connection was closed by the client
     * or the server.
     *
     * @param clientConnectionContext the connection to the client.
     * @param reason                  reason specified by the client for the DISCONNECT.
     */
    public void clientDisconnectedGracefully(
            final @NotNull ClientConnectionContext clientConnectionContext, final @Nullable String reason) {

        final String clientId = clientConnectionContext.getClientId();
        final String ip = clientConnectionContext.getChannelIP().orElse(null);

        if (log.isTraceEnabled()) {
            log.trace("Client {} disconnected gracefully.", clientId);
        }
        if (reason != null) {
            logClientDisconnected.debug("Client ID: {}, IP: {} disconnected gracefully. Reason given by client: {}",
                    valueOrUnknown(clientId),
                    valueOrUnknown(ip),
                    reason);
        } else {
            logClientDisconnected.debug("Client ID: {}, IP: {} disconnected gracefully.",
                    valueOrUnknown(clientId),
                    valueOrUnknown(ip));
        }
    }

    /**
     * Log that the connection to a client was closed ungracefully, regardless if the connection was closed by the
     * client or the server.
     *
     * @param clientConnectionContext the connection to the client.
     */
    public void clientDisconnectedUngracefully(final @NotNull ClientConnectionContext clientConnectionContext) {
        final String clientId = clientConnectionContext.getClientId();
        final String ip = clientConnectionContext.getChannelIP().orElse(null);
        final Listener listener = clientConnectionContext.getConnectedListener();
        final String listenerName = listener.readableName();
        final int listenerPort = listener.getPort();
        final String eventLogMessage = "Client ID: {}, IP: {} disconnected ungracefully from {} on port: {}.";

        if (log.isTraceEnabled()) {
            log.trace("Client {} disconnected ungracefully.", clientId);
        }
        logClientDisconnected.debug(eventLogMessage,
                valueOrUnknown(clientId),
                valueOrUnknown(ip),
                listenerName,
                listenerPort);
    }

    /**
     * Log that the connection to the client was closed by the broker.
     *
     * @param channel of the client connection
     * @param reason  why the connection was closed
     */
    public void clientWasDisconnected(@NotNull final Channel channel, @NotNull final String reason) {
        final ClientConnectionContext clientConnectionContext = ClientConnectionContext.of(channel);
        final String clientId = clientConnectionContext.getClientId();
        final String ip = clientConnectionContext.getChannelIP().orElse(null);
        if (log.isTraceEnabled()) {
            log.trace("Client {} was disconnected.", clientId);
        }
        logClientDisconnected.debug("Client ID: {}, IP: {} was disconnected. reason: {}.",
                valueOrUnknown(clientId),
                valueOrUnknown(ip),
                reason);
    }

    /**
     * Log that the an AUTH packet was received or sent.
     *
     * @param channel    of the client connection
     * @param reasonCode of the AUTH packet.
     */
    public void clientAuthentication(
            @NotNull final Channel channel, @NotNull final Mqtt5AuthReasonCode reasonCode, final boolean received) {
        final ClientConnectionContext clientConnectionContext = ClientConnectionContext.of(channel);
        final String clientId = clientConnectionContext.getClientId();
        final String ip = clientConnectionContext.getChannelIP().orElse(null);
        if (received) {
            logAuthentication.debug("Received AUTH from Client ID: {}, IP: {}, reason code: {}.",
                    valueOrUnknown(clientId),
                    valueOrUnknown(ip),
                    reasonCode.name());
        } else {
            logAuthentication.debug("Sent AUTH to Client ID: {}, IP: {}, reason code: {}.",
                    valueOrUnknown(clientId),
                    valueOrUnknown(ip),
                    reasonCode.name());
        }
    }

    /**
     * Log that a client session expired and will be deleted.
     *
     * @param expiryTimestamp the {@link Long} timestamp of the client-session-expiration
     * @param clientId        of the expired session
     */
    public void clientSessionExpired(final Long expiryTimestamp, @Nullable final String clientId) {

        final LocalDateTime disconnectedSinceDateTime =
                LocalDateTime.ofInstant(Instant.ofEpochMilli(expiryTimestamp), ZONE);
        logClientSessionExpired.debug(
                "Client ID: {} session has expired at {}. All persistent data for this client has been removed.",
                valueOrUnknown(clientId),
                disconnectedSinceDateTime.format(dateTimeFormatter));
    }

    @NotNull
    private String valueOrUnknown(@Nullable final Object object) {
        return object != null ? object.toString() : "UNKNOWN";
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy