LogHandler

LogHandler is the application-wide logging singleton. It accepts messages from any thread through a thread-safe interface and routes them to the in-app log display, an on-disk CSV log file, and (when enabled) a separate debug log file. Most code interacts with it exclusively through the free-function convenience wrappers; direct calls to the singleton are reserved for connection setup and experiment-lifecycle management.

Free-function API

Five free functions provide thread-safe logging from any context. They are declared in loghandler.h, which also pulls in using namespace Qt::Literals::StringLiterals, making _s and _L1 string-literal suffixes available in every translation unit that includes it.

bcLog(u"message"_s);                       // Normal severity (default)
bcLog(u"message"_s, LogHandler::Warning);  // Explicit severity
bcDebug(u"detail"_s);                      // Debug severity
bcWarn(u"condition"_s);                    // Warning severity
bcError(u"failure"_s);                     // Error severity
bcHighlight(u"milestone"_s);               // Highlight severity

Prefer these free functions over calling LogHandler::instance().log() directly. Do not use qDebug() or emit logMessage() in new code.

MessageCode severity

The MessageCode enum classifies each log entry:

Level

Use for

Normal

Connection outcomes, experiment milestones, user-initiated state changes

Warning

Automatically-corrected mismatches the user should know about

Error

Failures requiring user action or indicating data-loss risk

Highlight

Major milestones such as experiment start and end

Debug

Hardware lifecycle events, configuration loading, protocol details, parameter traces; written to the debug log file only when debug logging is enabled

HardwareObject helpers

HardwareObject subclasses use the member helpers hwLog, hwWarn, hwError, and hwDebug, which prepend the device key to every message before forwarding it to the corresponding free function — see HardwareObject for their signatures. These helpers are not part of LogHandler’s own API.

Singleton and instance method

LogHandler::instance() returns a reference to the application-wide singleton. The log(text, type) instance method is the underlying implementation that all free functions call. It is also the slot target for logMessage() and logMessageWithTime() shim slots that keep legacy emit logMessage(...) call sites compiling.

On-disk log files

LogHandler writes to two log files under the active data path:

  • Main log — receives all messages of severity Normal, Warning, Error, and Highlight.

  • Debug log — receives Debug-severity messages. Writing to this file is enabled only when debug logging is active (controlled at runtime via ApplicationConfigManager’s setDebugLogging(); the ApplicationConfigManager::debugLoggingChanged signal connects to LogHandler::setDebugLogging at application startup).

beginExperimentLog(num, msg) opens a per-experiment log file for the duration of an acquisition; endExperimentLog() closes it. These are called by MainWindow and BatchManager at the experiment lifecycle boundaries.

Display helpers

formatForDisplay(text, type, t) returns a formatted QString suitable for insertion into the in-app log text widget. The sendLogMessage signal carries this formatted string; iconUpdate carries the severity code so the tab icon can reflect the highest-severity unacknowledged message.

API Reference

class LogHandler : public QObject

Application-wide logging singleton.

LogHandler routes log messages to the in-app display, an on-disk main log file, and (when enabled) a separate debug log file. All public methods and the free-function wrappers are thread-safe.

Normal code should use the free functions (bcLog, bcDebug, bcWarn, bcError, bcHighlight) rather than calling instance() directly. HardwareObject subclasses should use the hwLog / hwWarn / hwError / hwDebug member helpers instead.

Public Types

enum MessageCode

Severity level for a log message.

Values:

enumerator Normal

Connection outcomes, experiment milestones, user-initiated state changes.

enumerator Warning

Automatically-corrected mismatches the user should know about.

enumerator Error

Failures requiring user action or indicating data-loss risk.

enumerator Highlight

Major milestones such as experiment start and end.

enumerator Debug

Hardware lifecycle, configuration loading, protocol details; debug-log only.

Public Functions

explicit LogHandler(bool logToFile = true, QObject *parent = nullptr)

Construct a LogHandler.

Parameters:
  • logToFile – Whether to write messages to disk (true by default).

  • parent – Qt parent object.

~LogHandler()
void log(QAnyStringView text, MessageCode type = Normal)

Log a message at the specified severity.

Accepts any string type accepted by QAnyStringView (QString, QStringView, QLatin1StringView, const char *) without constructing a temporary QString. Thread-safe.

Parameters:
  • text – Message text.

  • type – Severity level (default: Normal).

Public Slots

void logMessage(const QString &text, const MessageCode type = Normal)

Log a message; shim for legacy emit logMessage() call sites.

Forwards to log(). Prefer the free functions in new code.

Parameters:
  • text – Message text.

  • type – Severity level (default: Normal).

void logMessageWithTime(const QString &text, const MessageCode type = Normal, QDateTime t = QDateTime::currentDateTime())

Log a message with an explicit timestamp.

Forwards to log() using the supplied timestamp instead of the current time. Used when replaying stored messages at a known time.

Parameters:
  • text – Message text.

  • type – Severity level (default: Normal).

  • t – Explicit timestamp.

void beginExperimentLog(int num, const QString &msg)

Open a per-experiment log file for the duration of an acquisition.

Called at experiment start. All subsequent messages are also written to the experiment-specific log file until endExperimentLog() is called.

Parameters:
  • numExperiment number (used to derive the file path).

  • msg – Initial message written to the experiment log.

void endExperimentLog()

Close the per-experiment log file opened by beginExperimentLog().

void setDebugLogging(bool enabled)

Enable or disable writing of Debug-severity messages to the debug log file.

Connected to ApplicationConfigManager::debugLoggingChanged at application startup so runtime configuration changes propagate automatically.

Parameters:

enabled – True to write Debug messages to the debug log file.

Signals

void sendLogMessage(QString)

Emitted with the formatted message string for display.

Connect to a QTextEdit::append slot to update the in-app log view.

void iconUpdate(LogHandler::MessageCode)

Emitted with the severity of each new message.

Connect to update a tab icon or status indicator to reflect the highest-severity unacknowledged message.

Public Static Functions

static LogHandler &instance()

Return a reference to the application-wide singleton instance.

static QString formatForDisplay(const QString &text, MessageCode type, QDateTime t = QDateTime::currentDateTime())

Format a message for display in the in-app log widget.

Prepends a timestamp and a severity indicator to text. Called internally before emitting sendLogMessage.

Parameters:
  • text – Message text.

  • type – Severity level.

  • t – Timestamp (defaults to the current date/time).

Returns:

Formatted string ready for insertion into a QTextEdit.

Private Functions

void doLog(const QString &text, MessageCode type, const QDateTime &t)
void writeToFile(const QString &text, MessageCode type, const QDateTime &t)

Private Members

std::atomic<int> d_currentExperimentNum = {-1}
std::atomic<bool> d_logToFile = {true}
std::atomic<bool> d_debugLogging = {false}
QMutex d_fileMutex

Private Static Attributes

static LogHandler *s_instance = nullptr
void bcLog(QAnyStringView text, LogHandler::MessageCode type = LogHandler::Normal)

Log a message at Normal severity (or an explicit severity).

Thread-safe. Forwards to LogHandler::instance().log(). This is the preferred logging entry point for most code.

Parameters:
  • text – Message text (any string type accepted by QAnyStringView).

  • type – Severity level (default: LogHandler::Normal).

void bcDebug(QAnyStringView text)

Log a message at Debug severity.

The message is written to the debug log file only when debug logging is enabled via ApplicationConfigManager::setDebugLogging().

Parameters:

text – Message text.

void bcWarn(QAnyStringView text)

Log a message at Warning severity.

Parameters:

text – Message text.

void bcError(QAnyStringView text)

Log a message at Error severity.

Parameters:

text – Message text.

void bcHighlight(QAnyStringView text)

Log a message at Highlight severity.

Parameters:

text – Message text.