2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.logreader.internal.handler;
15 import static org.openhab.binding.logreader.internal.LogReaderBindingConstants.*;
17 import java.time.ZonedDateTime;
18 import java.util.regex.PatternSyntaxException;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.logreader.internal.config.LogReaderConfiguration;
23 import org.openhab.binding.logreader.internal.filereader.api.FileReaderListener;
24 import org.openhab.binding.logreader.internal.filereader.api.LogFileReader;
25 import org.openhab.binding.logreader.internal.searchengine.SearchEngine;
26 import org.openhab.core.library.types.DateTimeType;
27 import org.openhab.core.library.types.DecimalType;
28 import org.openhab.core.library.types.StringType;
29 import org.openhab.core.thing.ChannelUID;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.thing.ThingStatus;
32 import org.openhab.core.thing.ThingStatusDetail;
33 import org.openhab.core.thing.binding.BaseThingHandler;
34 import org.openhab.core.types.Command;
35 import org.openhab.core.types.RefreshType;
36 import org.openhab.core.types.State;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
41 * The {@link LogHandler} is responsible for handling commands, which are
42 * sent to one of the channels.
44 * @author Miika Jukka - Initial contribution
45 * @author Pauli Anttila - Rewrite
48 public class LogHandler extends BaseThingHandler implements FileReaderListener {
49 private final Logger logger = LoggerFactory.getLogger(LogHandler.class);
51 private final LogFileReader fileReader;
53 private @NonNullByDefault({}) LogReaderConfiguration configuration;
55 private @Nullable SearchEngine errorEngine;
56 private @Nullable SearchEngine warningEngine;
57 private @Nullable SearchEngine customEngine;
59 public LogHandler(Thing thing, LogFileReader fileReader) {
61 this.fileReader = fileReader;
65 public void handleCommand(ChannelUID channelUID, Command command) {
66 switch (channelUID.getId()) {
68 updateChannel(channelUID, command, errorEngine);
71 case CHANNEL_WARNINGS:
72 updateChannel(channelUID, command, warningEngine);
75 case CHANNEL_CUSTOMEVENTS:
76 updateChannel(channelUID, command, customEngine);
80 logger.debug("Unsupported command '{}' received for channel '{}'", command, channelUID);
85 public void initialize() {
86 String logDir = System.getProperty("openhab.logdir");
88 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
89 "Cannot determine system log directory.");
93 configuration = getConfigAs(LogReaderConfiguration.class);
94 configuration.filePath = configuration.filePath.replaceFirst("\\$\\{OPENHAB_LOGDIR\\}", logDir);
96 logger.debug("Using configuration: {}", configuration);
101 warningEngine = new SearchEngine(configuration.warningPatterns, configuration.warningBlacklistingPatterns);
102 errorEngine = new SearchEngine(configuration.errorPatterns, configuration.errorBlacklistingPatterns);
103 String customPatterns = configuration.customPatterns;
104 customEngine = new SearchEngine(customPatterns != null ? customPatterns : "",
105 configuration.customBlacklistingPatterns);
106 } catch (PatternSyntaxException e) {
107 logger.debug("Illegal search pattern syntax '{}'. ", e.getMessage(), e);
108 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, e.getMessage());
112 logger.debug("Start file reader");
115 fileReader.registerListener(this);
116 fileReader.start(configuration.filePath, configuration.refreshRate);
117 updateStatus(ThingStatus.ONLINE);
118 } catch (Exception e) {
119 logger.debug("Exception occurred during initalization: {}. ", e.getMessage(), e);
121 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, e.getMessage());
126 public void dispose() {
127 logger.debug("Stopping thing");
131 private void updateChannel(ChannelUID channelUID, Command command, @Nullable SearchEngine matcher) {
132 if (matcher != null) {
133 if (command instanceof DecimalType) {
134 matcher.setMatchCount(((DecimalType) command).longValue());
135 } else if (command instanceof RefreshType) {
136 updateState(channelUID.getId(), new DecimalType(matcher.getMatchCount()));
138 logger.debug("Unsupported command '{}' received for channel '{}'", command, channelUID);
141 logger.debug("Cannot update channel as SearchEngine is null.");
145 private void clearCounters() {
146 if (errorEngine != null) {
147 errorEngine.clearMatchCount();
149 if (warningEngine != null) {
150 warningEngine.clearMatchCount();
152 if (customEngine != null) {
153 customEngine.clearMatchCount();
157 private void updateChannelIfLinked(String channelID, State state) {
158 if (isLinked(channelID)) {
159 updateState(channelID, state);
163 private void shutdown() {
164 logger.debug("Stop file reader");
165 fileReader.unregisterListener(this);
170 public void fileNotFound() {
171 final String msg = String.format("Log file '%s' does not exist", configuration.filePath);
172 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, msg);
176 public void fileRotated() {
177 logger.debug("Log rotated");
178 updateChannelIfLinked(CHANNEL_LOGROTATED, new DateTimeType(ZonedDateTime.now()));
182 public void handle(@Nullable String line) {
187 if (!(thing.getStatus() == ThingStatus.ONLINE)) {
188 updateStatus(ThingStatus.ONLINE);
191 if (errorEngine != null && errorEngine.isMatching(line)) {
192 updateChannelIfLinked(CHANNEL_ERRORS, new DecimalType(errorEngine.getMatchCount()));
193 updateChannelIfLinked(CHANNEL_LASTERROR, new StringType(line));
194 triggerChannel(CHANNEL_NEWERROR, line);
196 if (warningEngine != null && warningEngine.isMatching(line)) {
197 updateChannelIfLinked(CHANNEL_WARNINGS, new DecimalType(warningEngine.getMatchCount()));
198 updateChannelIfLinked(CHANNEL_LASTWARNING, new StringType(line));
199 triggerChannel(CHANNEL_NEWWARNING, line);
201 if (customEngine != null && customEngine.isMatching(line)) {
202 updateChannelIfLinked(CHANNEL_CUSTOMEVENTS, new DecimalType(customEngine.getMatchCount()));
203 updateChannelIfLinked(CHANNEL_LASTCUSTOMEVENT, new StringType(line));
204 triggerChannel(CHANNEL_NEWCUSTOM, line);
209 public void handle(@Nullable Exception ex) {
210 final String msg = ex != null ? ex.getMessage() : "";
211 logger.debug("Error while trying to read log file: {}. ", msg, ex);
212 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, msg);