Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,9 @@ Makefile*
*.qmlproject.user.*

#QtCtreator CMake
CMakeLists.txt.user
CMakeLists.txt.user

# Python

__pycache__/
*.pyc
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,6 @@ install(
RUNTIME DESTINATION ${CCP_VENDOR_BIN_PATH}
BUNDLE DESTINATION ${CCP_VENDOR_BIN_PATH}
)

enable_testing()
add_subdirectory(tests)
28 changes: 16 additions & 12 deletions clients/python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,28 +57,32 @@ def __init__(self, server='127.0.0.1', pid=None, machine_name=None, executable_p
msg.type = _MessageType.CONNECTION_MESSAGE
msg.body.connection.version = VERSION
msg.body.connection.pid = pid
msg.body.connection.machine_name = machine_name
msg.body.connection.executable_path = executable_path
self.socket.sendall(buffer(msg)[:])
msg.body.connection.machine_name = machine_name.encode('utf-8') if isinstance(machine_name, str) else machine_name
msg.body.connection.executable_path = executable_path.encode('utf-8') if isinstance(executable_path, str) else executable_path
self.socket.sendall(bytes(msg))

def log(self, severity, message, timestamp=None, module='', channel=''):
msg = _Message()
msg.body.text.timestamp = timestamp or int(time.time() * 1000)
msg.body.text.severity = severity
msg.body.text.module = module
msg.body.text.channel = channel
if len(message) < 255:
msg.body.text.module = module.encode('utf-8') if isinstance(module, str) else module
msg.body.text.channel = channel.encode('utf-8') if isinstance(channel, str) else channel

# Encode message to bytes and handle length in bytes, not chars
message_bytes = message.encode('utf-8') if isinstance(message, str) else message

if len(message_bytes) < 255:
msg.type = _MessageType.SIMPLE_MESSAGE
msg.body.text.message = message
self.socket.sendall(buffer(msg)[:])
msg.body.text.message = message_bytes
self.socket.sendall(bytes(msg))
else:
offset = 0
msg.type = _MessageType.LARGE_MESSAGE
while offset < len(message):
msg.body.text.message = message[offset:offset + 255]
self.socket.sendall(buffer(msg)[:])
while offset < len(message_bytes):
msg.body.text.message = message_bytes[offset:offset + 255]
self.socket.sendall(bytes(msg))
offset += 255
if offset + 255 >= len(message):
if offset + 255 >= len(message_bytes):
msg.type = _MessageType.CONTINUATION_END_MESSAGE
else:
msg.type = _MessageType.CONTINUATION_MESSAGE
Expand Down
72 changes: 72 additions & 0 deletions clients/python/flood_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env python3

import argparse
import sys
from . import LogLiteClient, Severity


def main():
parser = argparse.ArgumentParser(
description='Flood a LogLite server with messages carrying monotonic sequence numbers.',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog='''
Examples:
python3 -m clients.python.flood_helper --count 10
python3 -m clients.python.flood_helper --count 200000 --host 127.0.0.1 --port 3273 --severity notice
'''
)

parser.add_argument(
'--count', '-c',
type=int,
default=10,
help='Number of messages to send (default: 10)'
)
parser.add_argument(
'--host',
default='127.0.0.1',
help='LogLite server host (default: 127.0.0.1)'
)
parser.add_argument(
'--port',
type=int,
default=3273,
help='LogLite server port (default: 3273, 0xCC9 in hex)'
)
parser.add_argument(
'--severity',
choices=['info', 'notice', 'warning', 'error'],
default='notice',
help='Message severity level (default: notice)'
)

args = parser.parse_args()

severity_map = {
'info': Severity.INFO,
'notice': Severity.NOTICE,
'warning': Severity.WARNING,
'error': Severity.ERROR,
}

try:
client = LogLiteClient(server=args.host, pid=None, machine_name=None, executable_path=None)

for seq in range(args.count):
message_text = 'msg {}'.format(seq)
client.log(
severity=severity_map[args.severity],
message=message_text,
module='flood_helper',
channel='test'
)

return 0

except Exception as e:
print('Error: {}'.format(e), file=sys.stderr)
return 1


if __name__ == '__main__':
sys.exit(main())
1 change: 1 addition & 0 deletions include/abstractlogmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ class AbstractLogModel: public QAbstractTableModel
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
protected:
void addMessage(LogMessage*);
void removeOldestRows(int count);
QVector<LogMessage*> m_messages;
bool m_breakLines;
private:
Expand Down
24 changes: 21 additions & 3 deletions include/logmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
#include "abstractlogmodel.h"
#include <QtNetwork/QTcpServer>

class QSettings;

// The integer values are persisted to disk and must stay stable across
// versions; new modes append (3+).
enum class RetentionMode
{
Unbounded = 0,
Server = 1,
Rolling = 2,
};

class LogModel : public AbstractLogModel
{
Expand Down Expand Up @@ -40,14 +50,22 @@ class LogModel : public AbstractLogModel
const Statistics &statistics() const;
bool isListening() const;

void setServerMode(bool serverMode);
bool isInServerMode() const;
void setRetentionMode(RetentionMode mode);
RetentionMode retentionMode() const;
void setMaxMessages(int maxMessages);
int maxMessages() const;
void setAutoSaveDirectory(const QString& autoSaveDirectory);
QString autoSaveDirectory() const;
int getRunningCount(LogSeverity severity);

// Migrates the legacy "serverMode" bool when the "retentionMode" key is
// absent; an out-of-range stored value falls back to Unbounded.
static RetentionMode retentionModeFromSettings(QSettings& settings);
protected:
void recordMessage(LogMessage* message);
void enforceRetentionPolicy();
private:
void evictOldest(int count);
bool autoSave();

class RunningCount
Expand All @@ -72,7 +90,7 @@ class LogModel : public AbstractLogModel
int m_maxMessages;
QString m_autoSaveDirectory;
bool m_listening;
bool m_serverMode;
RetentionMode m_retentionMode;
RunningCount m_runningCounts[SEVERITY_COUNT];
private slots:
void acceptConnection();
Expand Down
2 changes: 1 addition & 1 deletion include/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ private slots:
void saveFile();
void exportAsText();

void setServerMode();
void retentionModeActionTriggered();
void splitByPids();
void showSettings();
void showAboutDialog();
Expand Down
21 changes: 21 additions & 0 deletions src/abstractlogmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,27 @@ void AbstractLogModel::addMessage(LogMessage* message)
}
}

void AbstractLogModel::removeOldestRows(int count)
{
if (count > m_messages.size())
{
count = m_messages.size();
}
if (count <= 0)
{
return;
}
// rowCount() is m_messages.size() + 1: the extra is a trailing sentinel row
// the buffer does not back, so it must stay out of the removed range.
beginRemoveRows(QModelIndex(), 0, count - 1);
for (int i = 0; i < count; ++i)
{
delete m_messages[i];
}
m_messages.remove(0, count);
endRemoveRows();
}

void AbstractLogModel::refreshColorBackgroundTheme()
{
QVector<int> roles;
Expand Down
Loading