Dobby 3.0
Dobby “Docker based Thingy” is a tool for managing and running OCI containers using crun
|
Classes | |
struct | TokenBucket |
Public Member Functions | |
EthanLogClient (sd_event *loop, ContainerId &&id, std::string &&name, int fd, unsigned allowedLevels, unsigned rate, unsigned burstSize, const std::string &memCgroupMountPoint) | |
Constructs a logging client which represents one pipe. | |
EthanLogClient (const EthanLogClient &other)=delete | |
EthanLogClient (EthanLogClient &&other)=delete | |
EthanLogClient & | operator= (const EthanLogClient &other)=delete |
EthanLogClient & | operator= (EthanLogClient &&other)=delete |
void | setContainerPid (pid_t pid) |
Sets the base pid for the client's container. | |
bool | closed () const |
ContainerId | id () const |
Private Member Functions | |
int | pipeFdHandler (uint32_t revents) |
Callback called when data to read on the logging pipe. | |
void | processLogData () |
Process some log data from a client pipe. | |
int | processLogLevel (const char *field, ssize_t len, struct iovec *iov) const |
Process the log level field. | |
int | processPid (const char *field, ssize_t len, struct iovec *iov) const |
Process the pid field. | |
int | processTimestamp (const char *field, ssize_t len, struct iovec *iov) const |
Process the timestamp field. | |
int | processThreadName (const char *field, ssize_t len, struct iovec *iov) const |
Process the thread name field. | |
int | processCodeFile (const char *field, ssize_t len, struct iovec *iov) const |
Process the function name field. | |
int | processCodeFunction (const char *field, ssize_t len, struct iovec *iov) const |
Process the function name field. | |
int | processCodeLine (const char *field, ssize_t len, struct iovec *iov) const |
Process the line number field. | |
int | processMessage (const char *field, ssize_t len, struct iovec *iov) const |
Process the message field. | |
bool | shouldDrop () |
Returns true if the message should be dropped due to rate limiting. | |
pid_t | findRealPid (pid_t nsPid) const |
Attempts to find the pid number in the root pid namespace from a pid in the containers namespace. | |
std::set< pid_t > | getAllContainerPids () const |
Reads the set of all pids within the client's container. | |
pid_t | readNsPidFromProc (pid_t pid) const |
Given a pid (in global namespace) tries to find what it's namespace pid is. | |
Static Private Member Functions | |
static int | pipeFdHandler (sd_event_source *source, int fd, uint32_t revents, void *userData) |
Callback called when data to read on the logging pipe. | |
Private Attributes | |
const ContainerId | mContainerId |
const std::string | mName |
const int | mPipeFd |
const unsigned | mAllowedLevels |
sd_event_source * | mSource |
std::string | mIdentifier |
char | mMsgBuf [(MAX_LOG_MSG_LENGTH *2)] |
size_t | mMsgLen |
bool | mRateLimitingEnabled |
struct EthanLogClient::TokenBucket | mTokenBucket |
unsigned int | mDropped |
std::chrono::steady_clock::time_point | mFirstDropped |
std::chrono::steady_clock::time_point | mLastDropped |
std::string | mDefaultObjectPid |
std::string | mDefaultSyslogPid |
std::string | mCgroupPidsPath |
std::map< pid_t, pid_t > | mNsToRealPidMapping |
Static Private Attributes | |
static constexpr ssize_t | MAX_LOG_MSG_LENGTH = 512 |
static constexpr char | RECORD_DELIM = '\x1e' |
static constexpr char | FIELD_DELIM = '\x1f' |
EthanLogClient::EthanLogClient | ( | sd_event * | loop, |
ContainerId && | id, | ||
std::string && | name, | ||
int | fd, | ||
unsigned | allowedLevels, | ||
unsigned | rate, | ||
unsigned | burstSize, | ||
const std::string & | memCgrpMountPoint | ||
) |
Constructs a logging client which represents one pipe.
[in] | loop | The systemd event loop the plugin is running |
[in] | name | The name of the container, used to tag all log messages |
[in] | fd | The pipe fd, this class takes ownership of the pipe and closes it in the destructor |
[in] | allowedLevels | Bitmask of the allowed log levels. |
[in] | rate | The number of log messages allowed per second. |
[in] | burst | The maximum number of messages allowed in a burst. |
[in] | memCgrpMountPoint | Used to lookup the pids within a container for mapping namespaced pids to real pids. |
|
private |
Attempts to find the pid number in the root pid namespace from a pid in the containers namespace.
It's tricky getting the real pid of a process in a namespace, see the below link for more information: https://blogs.oracle.com/linux/translating-process-id-between-namespaces
However we can make some assumptions about our containers which make it slightly easier; the first is that we always have a memory cgroup setup for them, and secondly there aren't typically going to be lots of processes in our containers. So what we do is read the /sys/fs/cgroup/memory/<id>/cgroup.procs file to get all the processes within the container, then we read each of their /proc/<pid>/status files to extract the NSpid fields and then match them up.
To speed up the process, everytime this method is called and we don't have an existing mapping, then we re-create the full mapping. This helps flush out dead processes from the cache and also speed up subsequent lookups. However this could result in a bit of load in Dobby, if the client constantly sent invalid pid numbers to us ... or more likely there are lots of transient processes that log just a single line, like a shell script or something ... not sure what the solution for that is.
[in] | nsPid | The pid in the namespace of the container. |
|
private |
Reads the set of all pids within the client's container.
This reads the cgroup.pids file from the memory cgroup for the container.
|
staticprivate |
Callback called when data to read on the logging pipe.
Checks if the pipe is closed, if not then reads a block from the pipe.
|
private |
Callback called when data to read on the logging pipe.
Checks if the pipe is closed, if not then reads a block from the pipe.
|
private |
Process the function name field.
[in] | tok | The field minus the leading 'S' character |
[out] | func | Upon return will point to the function name string |
|
private |
Process the function name field.
[in] | tok | The field minus the leading 'F' character |
[out] | func | Upon return will point to the function name string |
|
private |
Process the line number field.
[in] | tok | The field minus the leading 'N' character |
[out] | lineno | Upon return will container the line number |
|
private |
Process some log data from a client pipe.
app | The app/client that has generated the data |
data | The string (or part of a string) sent by the client |
datalen | The number of characters received |
The log string(s) sent by the client library are formatted using ASCII separators, the format is (temporarily) described on the following confluence page: https://www.stb.bskyb.com/confluence/display/~grayb/Unified+Logging+on+the+STB
But for those that can't be bothered opening a browser, the following are the cliff notes:
\x1e - Character used to start and terminate a log message \x1f - Character used to delimit fields within the message string Each field within the message is prefixed with one of the following upper case characters that define the field type. L - Log level P - PID of app in hexadecimal (without 0x prefix) T - Timestamp from monotonic clock in hexadecimal (without 0x prefix) R - Name of the thread S - Name of the source file containing the log message F - Name of the function producing the log message N - The line number of the log producer M - The log message (mandatory but can be empty)
|
private |
Process the log level field.
[in] | field | The field minus the leading 'L' character |
[out] | iov | The formatted io vector for journald |
|
private |
Process the message field.
[in] | tok | The field minus the leading 'F' character |
[out] | message | Upon return will point to the message string |
|
private |
Process the pid field.
[in] | field | The field minus the leading 'P' character |
[in] | len | The length of the field. |
|
private |
Process the thread name field.
[in] | tok | The field minus the leading 'R' character |
[out] | thread | Upon return will point to the thread name |
|
private |
Process the timestamp field.
[in] | tok | The field minus the leading 'T' character |
[out] | ts | The calculated timestamp |
|
private |
Given a pid (in global namespace) tries to find what it's namespace pid is.
This reads the /proc/<pid>/status file, line NStgid.
[in] | pid | The real pid of the process to lookup. |
void EthanLogClient::setContainerPid | ( | pid_t | pid | ) |
Sets the base pid for the client's container.
This is used as the default pid to put in the log message if the client hasn't supplied a pid, or we couldn't resolve their pid in the global pid namespace.
pid | The base pid of the container. |
|
private |
Returns true if the message should be dropped due to rate limiting.
The rate limits are set in the constructor and the algorithm used is a token bucket type setup.