Skip to content

Commit

Permalink
Log|libdeng2|Client: Filtering log entries
Browse files Browse the repository at this point in the history
LogBuffer can now use a separate filter object to decide whether
a log entry should be entered into the buffer or not.

Added LogFilter with fine-grained filtering parameters that allow
for per-domain minimum levels and developer messages. App stores
its log filter in the persistent Config as the record “log.filter”.

Added the -vv and -vvv options to more conveniently enable the
verbosity level. If these try to go beyond ‘extra verbose’, the
developer messages are enabled automatically. The -devlog and
-nodevlog options enable/disable the developer messages separately.

Also, the Config module was cleaned up by moving the constants
for Log and Updater into their own modules. This way the constants
are not saved persistently.
  • Loading branch information
skyjake committed Jan 6, 2014
1 parent 3ced433 commit c8a6ca9
Show file tree
Hide file tree
Showing 24 changed files with 668 additions and 163 deletions.
2 changes: 2 additions & 0 deletions distrib/win32/setup.iss.template
Expand Up @@ -132,7 +132,9 @@ Source: "modules\appconfig.de"; DestDir: "{app}\modules"; Components: Engine
Source: "modules\bootstrap.de"; DestDir: "{app}\modules"; Components: Engine
Source: "modules\Config.de"; DestDir: "{app}\modules"; Components: Engine
Source: "modules\gui.de"; DestDir: "{app}\modules"; Components: Engine
Source: "modules\Log.de"; DestDir: "{app}\modules"; Components: Engine
Source: "modules\recutil.de"; DestDir: "{app}\modules"; Components: Engine
Source: "modules\Updater.de"; DestDir: "{app}\modules"; Components: Engine
Source: "data\doomsday.pk3"; DestDir: "{app}\data"; Components: Engine
Source: "data\graphics\background.pcx"; DestDir: "{app}\data\graphics"; Components: Engine
Source: "data\graphics\loading1.png"; DestDir: "{app}\data\graphics"; Components: Engine
Expand Down
4 changes: 3 additions & 1 deletion doomsday/client/client.pro
Expand Up @@ -886,7 +886,8 @@ SOURCES += \

DOOMSDAY_SCRIPTS += \
modules/appconfig.de \
modules/bootstrap.de
modules/bootstrap.de \
modules/Updater.de

OTHER_FILES += \
$$DOOMSDAY_SCRIPTS \
Expand All @@ -904,6 +905,7 @@ mod.files = \
$$DOOMSDAY_SCRIPTS \
$$DENG_MODULES_DIR/Config.de \
$$DENG_MODULES_DIR/gui.de \
$$DENG_MODULES_DIR/Log.de \
$$DENG_MODULES_DIR/recutil.de

# These fonts may be needed during the initial startup busy mode.
Expand Down
33 changes: 33 additions & 0 deletions doomsday/client/modules/Updater.de
@@ -0,0 +1,33 @@
# The Doomsday Engine Project -- Doomsday Client
#
# Copyright (c) 2014 Jaakko Keränen <jaakko.keranen@iki.fi>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# 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 <http://www.gnu.org/licenses/>.

#----------------------------------------------------------------------------
# UPDATER
#
# This module provides access to the built-in automatic updater of the
# Doomsday Client.

# Update frequency.
const DAILY = 0
const BIWEEKLY = 1
const WEEKLY = 2
const MONTHLY = 3
const AT_STARTUP = 4

# Update release channel.
const CHANNEL_STABLE = 0
const CHANNEL_UNSTABLE = 1
20 changes: 6 additions & 14 deletions doomsday/client/modules/appconfig.de
Expand Up @@ -82,24 +82,16 @@ def setDefaults(d)

# Defaults for the automatic updater.
record d.updater

const d.updater.DAILY = 0
const d.updater.BIWEEKLY = 1
const d.updater.WEEKLY = 2
const d.updater.MONTHLY = 3
const d.updater.AT_STARTUP = 4

const d.updater.CHANNEL_STABLE = 0
const d.updater.CHANNEL_UNSTABLE = 1

d.updater.frequency = d.updater.WEEKLY

import Updater
d.updater.frequency = Updater.WEEKLY
if Version.STABLE
d.updater.channel = d.updater.CHANNEL_STABLE
d.updater.channel = Updater.CHANNEL_STABLE
else
d.updater.channel = d.updater.CHANNEL_UNSTABLE
d.updater.channel = Updater.CHANNEL_UNSTABLE
end
d.updater.lastChecked = Time("")
d.updater.onlyManually = (Version.OS == 'unix')
d.updater.onlyManually = (Version.OS == 'unix')
d.updater.autoDownload = False
d.updater.delete = True
d.updater.downloadPath = "${DEFAULT}"
Expand Down
35 changes: 21 additions & 14 deletions doomsday/client/src/settingsregister.cpp
Expand Up @@ -139,25 +139,32 @@ DENG2_OBSERVES(App, GameChange)

QVariant getDefaultFromConfig(String const &name)
{
// Get defaults for script values.
Script script("record d; import Config; Config.setDefaults(d); d");
Process proc(script);
proc.execute();
try
{
// Get defaults for script values.
Script script("record d; import Config; Config.setDefaults(d); d");
Process proc(script);
proc.execute();

Record const &confDefaults = *proc.context().evaluator().result()
.as<RecordValue>().record();
Record const &confDefaults = *proc.context().evaluator().result()
.as<RecordValue>().record();

DENG2_ASSERT(confDefaults.has(name));
DENG2_ASSERT(confDefaults.has(name));

Variable const &var = confDefaults[name];
if(var.value().is<NumberValue>())
{
return var.value().asNumber();
Variable const &var = confDefaults[name];
if(var.value().is<NumberValue>())
{
return var.value().asNumber();
}
else
{
// Oops, we don't support this yet.
DENG2_ASSERT(false);
}
}
else
catch(Error const &er)
{
// Oops, we don't support this yet.
DENG2_ASSERT(false);
LOG_WARNING("Failed to find default for \"%s\": %s") << name << er.asText();
}
return QVariant();
}
Expand Down
2 changes: 2 additions & 0 deletions doomsday/libdeng2/include/de/LogFilter
@@ -0,0 +1,2 @@
#include "core/logfilter.h"

2 changes: 2 additions & 0 deletions doomsday/libdeng2/include/de/SimpleLogFilter
@@ -0,0 +1,2 @@
#include "core/logfilter.h"

2 changes: 1 addition & 1 deletion doomsday/libdeng2/include/de/c_wrapper.h
Expand Up @@ -102,7 +102,7 @@ DENG2_PUBLIC void LogBuffer_EnableStandardOutput(int enable);
DENG2_PUBLIC void LogBuffer_Flush(void);
DENG2_PUBLIC void LogBuffer_Clear(void);
DENG2_PUBLIC void LogBuffer_Msg(char const *text);
DENG2_PUBLIC void LogBuffer_Printf(int levelAndAudience, char const *format, ...);
DENG2_PUBLIC void LogBuffer_Printf(unsigned int metadata, char const *format, ...);

/*
* Info
Expand Down
3 changes: 3 additions & 0 deletions doomsday/libdeng2/include/de/core/app.h
Expand Up @@ -25,6 +25,7 @@
#include "../CommandLine"
#include "../NativePath"
#include "../LogBuffer"
#include "../LogFilter"
#include "../System"
#include "../FileSystem"
#include "../ScriptSystem"
Expand Down Expand Up @@ -134,6 +135,8 @@ class DENG2_PUBLIC App : DENG2_OBSERVES(Clock, TimeChange)

static App &app();

static LogFilter &logFilter();

/**
* Returns the command line used to start the application.
*/
Expand Down
57 changes: 30 additions & 27 deletions doomsday/libdeng2/include/de/core/log.h
Expand Up @@ -258,12 +258,13 @@ class DENG2_PUBLIC LogEntry : public Lockable, public ISerializable
{
public:
/**
* Target audience/domain of the entry (bits). If not set, the entry is generic and
* intended for the end-user/player.
* Entry domain (bits) and target audience. If not set, the entry is generic and intended for
* the end-user/player.
*/
enum Audience
enum Context
{
// Domains
Generic = 0, ///< Global domain.
Resource = 0x10000, /**< Resource or resource pack domain (files, etc.).
"Resource" is here meant in a wider sense of all the
external data that Doomsday utilizes. */
Expand All @@ -276,17 +277,19 @@ class DENG2_PUBLIC LogEntry : public Lockable, public ISerializable
Network = 0x400000, ///< Network domain: connections, packets, etc.

// User groups
Dev = 0x800000, /**< Native code developer (i.e., the programmer); can be
combined with other flags to mark the entry for devs */
Dev = 0x8000000, /**< Native code developer (i.e., the programmer); can be
combined with other flags to mark the entry for devs.
If bit is not set, the entry is for the end-user. */

DomainMask = 0x07f0000,
AudienceMask = 0xfff0000
AllDomains = 0x7f0000,
DomainMask = AllDomains,
ContextMask = 0xfff0000
};

static String audienceToText(duint32 audience)
static String contextToText(duint32 context)
{
String const suffix = (audience & Dev? "Dev" : "");
switch(audience & DomainMask)
String const suffix = (context & Dev? "Dev" : "");
switch(context & DomainMask)
{
case Resource: return "Resource" + suffix;
case Map: return "Map" + suffix;
Expand All @@ -299,7 +302,7 @@ class DENG2_PUBLIC LogEntry : public Lockable, public ISerializable
}
}

static Audience textToAudience(String text)
static Context textToContext(String text)
{
duint32 val = 0;
if(text.endsWith("Dev"))
Expand All @@ -309,10 +312,10 @@ class DENG2_PUBLIC LogEntry : public Lockable, public ISerializable
}
for(int i = 16; i < 32; ++i)
{
if(!audienceToText(Audience(1 << i)).compareWithoutCase(text))
return Audience((1 << i) | val);
if(!contextToText(LogEntry::Context(1 << i)).compareWithoutCase(text))
return LogEntry::Context((1 << i) | val);
}
throw de::Error("Log::textToAudience", "'" + text + "' is not a valid log audience");
throw de::Error("Log::textToContext", "'" + text + "' is not a valid log entry context");
}

/// Importance level of the log entry.
Expand Down Expand Up @@ -359,8 +362,8 @@ class DENG2_PUBLIC LogEntry : public Lockable, public ISerializable
Critical = 7,

MAX_LOG_LEVELS,
LOWEST_LOG_LEVEL = XVerbose,

LOWEST_LOG_LEVEL = XVerbose,
LevelMask = 0x7
};

Expand Down Expand Up @@ -520,7 +523,7 @@ class DENG2_PUBLIC LogEntry : public Lockable, public ISerializable
*/
LogEntry();

LogEntry(duint32 levelAndAudience, String const &section, int sectionDepth, String const &format, Args args);
LogEntry(duint32 metadata, String const &section, int sectionDepth, String const &format, Args args);

/**
* Copy constructor.
Expand All @@ -537,9 +540,9 @@ class DENG2_PUBLIC LogEntry : public Lockable, public ISerializable
/// Returns the timestamp of the entry.
Time when() const { return _when; }

inline duint32 audience() const { return _levelAudience & AudienceMask; }
inline duint32 audience() const { return _metadata & ContextMask; }

inline Level level() const { return Level(_levelAudience & LevelMask); }
inline Level level() const { return Level(_metadata & LevelMask); }

/// Returns a reference to the entry's section part. Reference is valid
/// for the lifetime of the entry.
Expand Down Expand Up @@ -569,7 +572,7 @@ class DENG2_PUBLIC LogEntry : public Lockable, public ISerializable

private:
Time _when;
duint32 _levelAudience;
duint32 _metadata;
String _section;
int _sectionDepth;
String _format;
Expand Down Expand Up @@ -644,12 +647,12 @@ class DENG2_PUBLIC Log
/**
* Creates a new log entry with the specified log entry level.
*
* @param levelAndAudience Level of the entry and target audience bits.
* @param format Format template of the entry.
* @param arguments List of arguments. The entry is given ownership of
* each Arg instance.
* @param metadata Level of the entry and context bits.
* @param format Format template of the entry.
* @param arguments List of arguments. The entry is given ownership of
* each Arg instance.
*/
LogEntry &enter(duint32 levelAndAudience, String const &format, LogEntry::Args arguments = LogEntry::Args());
LogEntry &enter(duint32 metadata, String const &format, LogEntry::Args arguments = LogEntry::Args());

public:
/**
Expand Down Expand Up @@ -679,7 +682,7 @@ class DENG2_PUBLIC Log
class DENG2_PUBLIC LogEntryStager
{
public:
LogEntryStager(duint32 levelAndAudience, String const &format);
LogEntryStager(duint32 metadata, String const &format);

/// Appends a new argument to the entry.
template <typename ValueType>
Expand All @@ -694,13 +697,13 @@ class DENG2_PUBLIC LogEntryStager
~LogEntryStager() {
if(!_disabled) {
// Ownership of the entries is transferred to the LogEntry.
LOG().enter(_level, _format, _args);
LOG().enter(_metadata, _format, _args);
}
}

private:
bool _disabled;
duint32 _level;
duint32 _metadata;
String _format;
LogEntry::Args _args;
};
Expand Down
41 changes: 33 additions & 8 deletions doomsday/libdeng2/include/de/core/logbuffer.h
Expand Up @@ -49,6 +49,26 @@ class DENG2_PUBLIC LogBuffer : public QObject, public Lockable, DENG2_OBSERVES(F
public:
typedef QList<LogEntry const *> Entries;

/**
* Interface for objects that filter log entries.
*/
class IFilter
{
public:
virtual ~IFilter() {}

/**
* Determines if a log entry should be allowed into the log buffer
* if it has a particular set of metadata. Note that this method
* will be called from several threads, so it needs to be thread-safe.
*
* @param metadata Entry metadata.
*
* @return @c true, to allow entering into the buffer.
*/
virtual bool isLogEntryAllowed(duint32 metadata) const = 0;
};

public:
/**
* Constructs a new log buffer. By default log levels starting with MESSAGE
Expand Down Expand Up @@ -96,18 +116,23 @@ class DENG2_PUBLIC LogBuffer : public QObject, public Lockable, DENG2_OBSERVES(F
void latestEntries(Entries &entries, int count = 0) const;

/**
* Enables log entries at or over a level. When a level is disabled, the
* entries will not be added to the log entry buffer.
* Sets the filter that determines if a log entry will be permitted into
* the buffer.
*
* @param entryFilter Filter object. @c NULL to use the default filter.
*/
void enable(LogEntry::Level overLevel = LogEntry::Message);
void setEntryFilter(IFilter const *entryFilter);

/**
* Disables the log.
* @see enable()
* Checks if a log entry will be enabled if it has a particular set of
* context metadata bits.
*
* @param entryMetadata Metadata of an entry.
*
* @return @c true, if the entry should be added to the buffer. @c false,
* if the entry should be ignored.
*/
void disable() { enable(LogEntry::MAX_LOG_LEVELS); }

bool isEnabled(LogEntry::Level overLevel = LogEntry::Message) const;
bool isEnabled(duint32 entryMetadata = LogEntry::Message) const;

/**
* Enables or disables standard output of log messages. When enabled,
Expand Down

0 comments on commit c8a6ca9

Please sign in to comment.