From 659477920e8fdbfee6e2776254d8f0463e7723fb Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sat, 22 Jul 2017 11:36:50 +0100 Subject: Documentation in the header --- docs/.gitignore | 1 + docs/Doxyfile | 2 +- include/nslog/nslog.h | 369 +++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 353 insertions(+), 19 deletions(-) create mode 100644 docs/.gitignore diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..1936cc1 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +html diff --git a/docs/Doxyfile b/docs/Doxyfile index ad5175b..29be490 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -655,7 +655,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = include src +INPUT = include # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is diff --git a/include/nslog/nslog.h b/include/nslog/nslog.h index 10b90a8..045914c 100644 --- a/include/nslog/nslog.h +++ b/include/nslog/nslog.h @@ -17,6 +17,17 @@ #include +/** + * Log levels + * + * When logging, you can set the 'log level' which can be used as a basic way + * to filter the logging which occurs. In addition, the logging level can be + * used to control which logging is compiled in. As such, release builds can + * be made where DEBUG and DEEPDEBUG are compiled out. + * + * If logging is set at INFO then everything above INFO will be logged, + * including warnings, errors, etc. + */ typedef enum { NSLOG_LEVEL_DEEPDEBUG = 0, NSLOG_LEVEL_DEBUG = 1, @@ -27,6 +38,14 @@ typedef enum { NSLOG_LEVEL_CRITICAL = 6, } nslog_level; +/** + * Convert a logging level to a string. + * + * The returned string is owned by the nslog library (static) and should + * not be freed. + * + * \param level The level for which you want the 'canonical' name. + */ const char *nslog_level_name(nslog_level level); #define NSLOG_LEVEL_DD NSLOG_LEVEL_DEEPDEBUG @@ -37,31 +56,75 @@ const char *nslog_level_name(nslog_level level); #define NSLOG_LEVEL_CRIT NSLOG_LEVEL_CRITICAL #ifndef NSLOG_COMPILED_MIN_LEVEL +/** + * The minimum log level to be compiled in. + * + * When compiling a library or application which uses nslog, you can set the + * minimum level to be compiled in. By setting this, you can reduce the + * size of the binary and potentially improve the performance of hotspots + * which contain logging instructions. + */ #define NSLOG_COMPILED_MIN_LEVEL NSLOG_LEVEL_DEBUG #endif +/** + * Logging category + * + * This structure is used internally by nslog to manage categories for logging. + * Categories are declared by the \ref NSLOG_DECLARE_CATEGORY and defined by + * either \ref NSLOG_DEFINE_CATEGORY and \ref NSLOG_DEFINE_SUBCATEGORY. + * + * It is not recommended that clients of nslog look inside the category structs + * excepting the name (and namelen) values. In addition you should only trust + * those values when handling a log callback. + */ typedef struct nslog_category_s { - const char *cat_name; - const char *description; - struct nslog_category_s *parent; - char *name; - int namelen; - struct nslog_category_s *next; + const char *cat_name; /**< The category leaf name (static) */ + const char *description; /**< The category description (static) */ + struct nslog_category_s *parent; /**< The category's parent (static) */ + char *name; /**< The fully qualified category name (owned by nslog) */ + int namelen; /**< The length of the category name */ + struct nslog_category_s *next; /**< Link to next category (internal) */ } nslog_category_t; +/** + * Log entry context + * + * When logging, nslog will create an entry context and pass it to the log + * callback. This context tells you everything about the log entry being + * created (except the message) and is ephemeral (if you need the content + * beyond the scope of the log callback, copy it). + */ typedef struct nslog_entry_context_s { - nslog_category_t *category; - nslog_level level; - const char *filename; - int filenamelen; - const char *funcname; - int funcnamelen; - int lineno; + nslog_category_t *category; /**< The category under which the log entry is being made */ + nslog_level level; /**< The level at which the log entry is being made */ + const char *filename; /**< The source filename where the log entry is */ + int filenamelen; /**< The source filename name's length */ + const char *funcname; /**< The source function where the log entry is */ + int funcnamelen; /**< The source function name's length */ + int lineno; /**< The line number at which the log entry is */ } nslog_entry_context_t; +/** + * Declare a category + * + * This macro is used to declare a category. Use it in headers to allow more + * than one source file to use the same category. + * + * \param catname The category leaf name (as a bareword) + */ #define NSLOG_DECLARE_CATEGORY(catname) \ extern nslog_category_t __nslog_category_##catname +/** + * Define a category + * + * This macro is used to define the storage for a category. Use it in one of + * your C source files to allow the category to have some storage space. + * + * \param catname The category name (as a bareword) + * \param description The category description (as a static string) + */ #define NSLOG_DEFINE_CATEGORY(catname, description) \ nslog_category_t __nslog_category_##catname = { \ #catname, \ @@ -72,6 +135,19 @@ typedef struct nslog_entry_context_s { NULL, \ } +/** + * Define a sub-category + * + * This macro is used to define the storage for a category which has a parent. + * Use it in one of your C source files to allow the category to have some + * storage space. Sub-categories end up named by their parent name and their + * name separated by slashes. This is arbitrary in depth, allowing sub-sub-sub + * categories etc. + * + * \param parentcatname The category name of the parent category (as a bareword) + * \param catname The category name (as a bareword) + * \param description The category description (as a static string) + */ #define NSLOG_DEFINE_SUBCATEGORY(parentcatname, catname, description) \ nslog_category_t __nslog_category_##catname = { \ #catname, \ @@ -82,10 +158,22 @@ typedef struct nslog_entry_context_s { NULL, \ } +/** + * Log something + * + * This is the primary logging macro in nslog. It needs access to the category + * which is to be used to log, hence header files and the \ref + * NSLOG_DECLARE_CATEGORY macro. + * + * \param catname The category name (as a bareword) + * \param level The level at which this is logged (as a bareword such as WARNING) + * \param logmsg The log message itself (printf format string) + * \param args The arguments for the log message + */ #define NSLOG(catname, level, logmsg, args...) \ do { \ if (NSLOG_LEVEL_##level >= NSLOG_COMPILED_MIN_LEVEL) { \ - nslog_entry_context_t ctx = { \ + static nslog_entry_context_t ctx = { \ &__nslog_category_##catname, \ NSLOG_LEVEL_##level, \ __FILE__, \ @@ -98,57 +186,302 @@ typedef struct nslog_entry_context_s { } \ } while(0) +/** + * Internal logging function + * + * While clients of nslog will not call this function directly (preferring to + * use the \ref NSLOG macro, this is the actual function which implements it. + * + * \param ctx The log entry context for the log + * \param pattern The printf message pattern + * \param ... The arguments for the log entry + */ void nslog__log(nslog_entry_context_t *ctx, const char *pattern, ...) __attribute__ ((format (printf, 2, 3))); +/** + * Log error types + * + * When nslog is performing actions which can allocate memory or otherwise + * rely on internal state machines. Such functions always return an error + * code and any extra values in pointer-based out-parameters. + */ typedef enum { - NSLOG_NO_ERROR = 0, - NSLOG_NO_MEMORY = 1, - NSLOG_UNCORKED = 2, - NSLOG_PARSE_ERROR = 3, + NSLOG_NO_ERROR = 0, /**< Nothing went wrong. Have a party. */ + NSLOG_NO_MEMORY = 1, /**< nslog ran out of memory. Worry, a great deal */ + NSLOG_UNCORKED = 2, /**< nslog is already uncorked, don't rush it */ + NSLOG_PARSE_ERROR = 3, /**< nslog failed to parse the given log filter */ } nslog_error; +/** + * Callback type for logging + * + * In order for a logging client to actually receive the messages which are + * logged, the client must register a logging callback. It is recommended that + * only the highest level client do so (for example NetSurf) but test suites + * can also register for log messages if you want to use that as part of your + * testing. + * + * \param context The context pointer registered for the callback + * \param ctx The log entry context + * \param fmt The log message (printf style format string) + * \param args The printf arguments for the log entry + */ typedef void (*nslog_callback)(void *context, nslog_entry_context_t *ctx, const char *fmt, va_list args); +/** + * Set the render callback for logging + * + * In order for the client to receive the log messages, it needs to register + * a \ref nslog_callback function. + * + * \param cb The callback function pointer + * \param context The context pointer to provide to the callback + * \return Whether or not this succeeded + */ nslog_error nslog_set_render_callback(nslog_callback cb, void *context); +/** + * Uncork the log + * + * When nslog starts up, it is corked which means that any messages logged will + * be saved up ready for when the client calls this function. Corked messages + * will be rendered when logged, so don't expect the format strings to be + * identical between logging before and after uncorking. + * + * Any stored log messages will be drained before this function returns. + * + * \return Whether or not the uncorking succeeded. + */ nslog_error nslog_uncork(void); +/** + * Log filter handle + * + * nslog allows clients to set a complex filter which can be used to restrict + * the log messages which make their way through to the log callback. + * + * Clients can build log filters up "by hand" or can pass a string + * representation of a log filter to the internal nslog parser. + * + * Log filters are reference counted and filter builders will return + * a log filter with a reference, and will take automatically reference + * any filters passed in, so remember to unref them if you're done. + */ typedef struct nslog_filter_s nslog_filter_t; +/** + * Create a category based filter + * + * The returned filter matches against the fully qualified category name + * and succeeds if the given category name is either an exact match or + * is a proper prefix of the category. + * + * For example, a filter of 'foo/bar' will match 'foo/bar' 'foo/bar/baz' + * but not 'foo' or 'foo/barfle'. + * + * \param catname The category name to filter on + * \param filter A pointer to a filter to be filled out + * \return Whether or not this succeeds + */ nslog_error nslog_filter_category_new(const char *catname, nslog_filter_t **filter); +/** + * Create a log level based filter + * + * The returned filter matches against the level of the log entry and succeeds + * if the entry's level is at least the value of the filter. + * + * For example, a filter of \ref NSLOG_LEVEL_WARNING will match log entries + * of the same level or higher (such as \ref NSLOG_LEVEL_ERROR) + * but not lower levels (such as \ref NSLOG_LEVEL_DEBUG) + * + * \param level The log level to filter at + * \param filter A pointer to a filter to be filled out + * \return Whether or not this succeeds + */ nslog_error nslog_filter_level_new(nslog_level level, nslog_filter_t **filter); + +/** + * Create a filename based filter + * + * The returned filter matches against the filename and succeeds + * if the entry's filename leafname matches the value of the filter. + * + * For example, a filter of "foo/bar.c" will match log entries + * for "foo/bar.c" or "baz/foo/bar.c" but not "baz/bar.c". + * + * \param filename The filename to filter with + * \param filter A pointer to a filter to be filled out + * \return Whether or not this succeeds + */ nslog_error nslog_filter_filename_new(const char *filename, nslog_filter_t **filter); + +/** + * Create a dirname based filter + * + * The returned filter matches against the filename and succeeds + * if the entry's filename dirname matches the value of the filter. + * + * For example, a filter of "foo" will match log entries + * for "foo/bar.c" or "foo/bar/baz.c" but not "baz/foo.c". + * + * \param dirname The directory name to filter with + * \param filter A pointer to a filter to be filled out + * \return Whether or not this succeeds + */ nslog_error nslog_filter_dirname_new(const char *dirname, nslog_filter_t **filter); + +/** + * Create a function-name based filter + * + * The returned filter matches against the name of the function + * where the \ref NSLOG statement exists, and succeeds if the + * entry's function name exactly matches the value of the filter. + * + * For example, a filter of "foo" will match log entries + * for `foo()` or `foo(int)` but not `foobar()`. + * + * \param funcname The function name to filter on + * \param filter A pointer to a filter to be filled out + * \return Whether or not this succeeds + */ nslog_error nslog_filter_funcname_new(const char *funcname, nslog_filter_t **filter); +/** + * Create a logical 'and' of two filters. + * + * The returned filter succeeds if both the passed in filters + * pass. It will short-circuit and not evaluate the `right` + * filter if the `left` filter fails. + * + * \param left The first filter to check + * \param right The second filter to check + * \param filter A pointer to a filter to be filled out + * \return Whether or not this succeeds + */ nslog_error nslog_filter_and_new(nslog_filter_t *left, nslog_filter_t *right, nslog_filter_t **filter); + +/** + * Create a logical 'or' of two filters. + * + * The returned filter succeeds if either of the passed in filters + * pass. It will short-circuit and not evaluate the `right` + * filter if the `left` filter succeeds. + * + * \param left The first filter to check + * \param right The second filter to check + * \param filter A pointer to a filter to be filled out + * \return Whether or not this succeeds + */ nslog_error nslog_filter_or_new(nslog_filter_t *left, nslog_filter_t *right, nslog_filter_t **filter); + +/** + * Create a logical 'xor' of two filters. + * + * The returned filter succeeds if one, or the other, + * but not both the passed in filters pass. + * + * \param left The first filter to check + * \param right The second filter to check + * \param filter A pointer to a filter to be filled out + * \return Whether or not this succeeds + */ nslog_error nslog_filter_xor_new(nslog_filter_t *left, nslog_filter_t *right, nslog_filter_t **filter); + +/** + * Create a logical 'not' of a filters. + * + * The returned filter succeeds if the passed in filter + * fails. + * + * \param input The first filter to check + * \param filter A pointer to a filter to be filled out + * \return Whether or not this succeeds + */ nslog_error nslog_filter_not_new(nslog_filter_t *input, nslog_filter_t **filter); +/** + * Take a reference to the passed in filter. + * + * This increments the reference count of the given filter + * and returns it directly to encourage a style of: + * + * `myfilter = nslog_filter_ref(incomingfilter)` + * + * \param filter A pointer to a filter to be referenced + * \return Whether or not this succeeds + */ nslog_filter_t *nslog_filter_ref(nslog_filter_t *filter); + +/** + * Release a reference to the passed in filter. + * + * This decrements the reference count of the given filter + * and returns NULL to encourage the style of: + * + * `myfilter = nslog_filter_ref(myfilter)` + * + * \param filter A pointer to a filter to be dereferenced + * \return Whether or not this succeeds + */ nslog_filter_t *nslog_filter_unref(nslog_filter_t *filter); +/** + * Set the passed in filter as the active filter + * + * This sets the active filter for managing the log. + * If you don't pass `NULL` in prev, then the currently + * active filter is returned for you to reuse later. + * + * \param filter A pointer to a filter to be set active + * \param prev A pointer which will be optionally filled out + * \return Whether or not this succeeds + */ nslog_error nslog_filter_set_active(nslog_filter_t *filter, nslog_filter_t **prev); + +/** + * Render a filter as its canonical textual form. + * + * Filters can be written in plain text and this function + * turns filters into their canonical textual form. + * + * The caller owns the returned string and must `free()` it. + * + * \param filter A pointer to a filter to be rendered as text + * \return A string representing the filter + */ char *nslog_filter_sprintf(nslog_filter_t *filter); +/** + * Parse a filter's textual form. + * + * Filters can be written in plain text and this function + * turns textual filters into filters which can be used + * by nslog. + * + * The caller owns the reference returned to it and must + * unreference the filter when done with it. + * + * \param input A pointer to filter text to be parsed + * \param output A pointer to fill out with the parsed filter + * \return Whether or not this succeeds + */ nslog_error nslog_filter_from_text(const char *input, nslog_filter_t **output); -- cgit v1.2.3