MasterSetupLog

Keeps the dated record of the important user actions of a master setup on the very hidden __log worksheet. It is the master setup copy of LLLog. Each event is one Checking bundle: the bundle title is the SECTION the action belongs to, the entry label carries the timestamp, the action code and a short detail, and the scope carries the outcome. Every bundle is flushed through CheckingOutput.PrintOutput in compact mode, so the sheet grows one line per event and the status and title dropdowns of the output sheet filter the log by outcome and by section.

WHAT ONE LINE SAYS

An entry line reads as the timestamp and the platform, then the procedure that raised the event, then the action code and the detail:

2026-08-26 13:55:45 win-64 excel-16.0 clickAddSheet > add-disease: Measles

The procedure is named by the caller. VBA carries no call stack to read a name from, so a line that names only the action leaves the reader guessing which button wrote it. The name is optional: a caller that passes none writes a line opening at the action code.

THE THREE SECTIONS

The log is read by three sections, and the title of an entry is the section the action belongs to. The writer runs in compact mode, so a section title is written the first time the sheet carries it and never again.

open/close the workbook open, the workbook close, a log restart data input/output every export, every import, the disease comparison master setup lifecycle everything else the user does in the file

An action code no case names lands in master setup lifecycle, so a new logged action reaches the sheet without touching this class. A code that opens with export- or import- lands in data input/output for the same reason.

PROVISIONING

Create(wkb) binds to the __log worksheet of the given workbook and builds it very hidden when the workbook has none, so an older master setup grows its log on its first logged event. Building a sheet wants the workbook structure unprotected, which is the duty of the caller.

ROTATION

LOG_MAX_ENTRIES is read as a row cap. A flush that finds the last written row past the cap clears the sheet and removes the row marker CheckingOutput keeps, so the next render re-initialises the sheet, and the fresh log opens with one info line ("restarted").

TEXT EXPORT

A master setup has no Metadata sheet, so the header of the text export carries the file name, the date, the platform and the count of disease worksheets, then the entries in the order they were written.

FILTER DROPDOWNS

The interactive row hiding of the output sheet wants a Worksheet_Change handler injected through the VBE, and injecting code wants VBE trust, which field machines lack. The worksheet code module is left alone; the dropdowns stay plain validation cells and the filtering is reachable through CheckingOutput.FilterWorksheet.

Version: 1.0 (2026-08-26)

Factory

Create #

create

Create a log store bound to the __log worksheet of a workbook

Signature:

Public Function Create(ByVal wkb As Workbook) As MasterSetupLog

Entry point for creating MasterSetupLog instances.

Binds to the __log worksheet of the given workbook. When the workbook has none, the sheet is appended after the last worksheet and made very hidden, so an older master setup grows its log on first use.

Parameters:

  • wkb: Workbook. The master setup workbook carrying the log.

Returns: MasterSetupLog. Fully initialised instance.

Throws:

  • ProjectError.ObjectNotInitialized When wkb is Nothing.

PublicAccessors

SheetName #

sheet-name

Name of the log worksheet

Signature:

Public Property Get SheetName() As String

Answers the internal sheet name so the Open Log door and the deploy list reach the sheet through the class.

Returns: String. The log worksheet name.


SectionOf #

section-of

The section an action code is logged under

Signature:

Public Function SectionOf(ByVal action As String) As String

The three sections are the whole title vocabulary of the log sheet. The workbook open, the workbook close and a log restart are the session boundaries and read as open/close; every export, every import and the disease comparison move data in or out of the file and read as data input/output; every other action the user takes in the file reads as master setup lifecycle, which is also where an action code named by no case below lands.

Parameters:

  • action: String. The action code of the event.

Returns: String. One of the three section titles.


MaxEntries #

max-entries

Row cap of the log sheet

Signature:

Public Property Get MaxEntries() As Long

Answers the cap the rotation reads, so a test can seed a row past it without writing ten thousand lines.

Returns: Long. The row cap.


DiseaseSheetCount #

disease-sheet-count

How many disease worksheets the workbook carries

Signature:

Public Property Get DiseaseSheetCount() As Long

A disease worksheet is one whose hidden sheetTag name reads disease, the same reading MasterSetupHelpers.IsMasterDiseaseSheet makes. The count heads the text export.

Returns: Long. The count of disease worksheets.


Logging

LogSuccess #

log-success

Log an action that ended well

Signature:

Public Sub LogSuccess(ByVal action As String, _
                      Optional ByVal detail As String = vbNullString, _
                      Optional ByVal source As String = vbNullString)

One method per outcome, each flushing one Checking bundle.

Parameters:

  • action: String. The action code, written on the entry line.
  • detail: Optional String. Short free text beside the timestamp.
  • source: Optional String. The procedure that raised the event.

LogWarning #

log-warning

Log a refusal or a degraded run

Signature:

Public Sub LogWarning(ByVal action As String, _
                      Optional ByVal detail As String = vbNullString, _
                      Optional ByVal source As String = vbNullString)

Parameters:

  • action: String. The action code, written on the entry line.
  • detail: Optional String. Short free text beside the timestamp.
  • source: Optional String. The procedure that raised the event.

LogFailure #

log-failure

Log an action that failed

Signature:

Public Sub LogFailure(ByVal action As String, _
                      Optional ByVal detail As String = vbNullString, _
                      Optional ByVal source As String = vbNullString)

Parameters:

  • action: String. The action code, written on the entry line.
  • detail: Optional String. Short free text beside the timestamp.
  • source: Optional String. The procedure that raised the event.

LogInfo #

log-info

Log a lifecycle line

Signature:

Public Sub LogInfo(ByVal action As String, _
                   Optional ByVal detail As String = vbNullString, _
                   Optional ByVal source As String = vbNullString)

Parameters:

  • action: String. The action code, written on the entry line.
  • detail: Optional String. Short free text beside the timestamp.
  • source: Optional String. The procedure that raised the event.

Export

ReportLines #

report-lines

The whole log as plain text, one line per element

Signature:

Public Function ReportLines() As BetterArray

Writing the log out as a file a user can send on.

The text a user sends on when something goes wrong, so it stands on its own: the workbook it came from, when and on what platform it was written, how many disease worksheets the file carries, then the log itself in the order it was written. The text is built here so what a file will say can be read without a file being written.

Returns: BetterArray. The lines, lower bound 1.

Throws:

  • ProjectError.ObjectNotInitialized When the log has no worksheet.

ExportText #

export-text

Write the whole log out as a plain text file

Signature:

Public Function ExportText(ByVal folderPath As String, _
                           Optional ByVal baseName As String = vbNullString) As String

The file lands in the given folder as -log.txt. A second export of the same master setup overwrites the first, which is what a person sending "the log" wants. What goes into it is ReportLines. This routine only opens the file, writes the lines and closes it.

Parameters:

  • folderPath: String. The folder taking the file.
  • baseName: String. The file name without its suffix. The workbook name when empty.

Returns: String. The full path of the written file.

Throws:

  • ProjectError.ObjectNotInitialized When the log has no worksheet.
  • ProjectError.InvalidArgument When no folder is given.

Internal members (not exported)

PublicAccessors

Wksh #

wksh

Retrieve the log worksheet backing this store

Signature:

Public Property Get Wksh() As Worksheet

Properties that expose internal state.

Returns: Worksheet. Reference to the log worksheet.


Wksh #

wksh-set

Define the worksheet that will receive the log

Signature:

Public Property Set Wksh(ByVal sh As Worksheet)

Parameters:


Internals

ResolveLogSheet #

resolve-log-sheet

Resolve the log worksheet, building it very hidden when absent

Signature:

Private Function ResolveLogSheet(ByVal wkb As Workbook) As Worksheet

Sheet resolution, entry building, and rotation.

The sheet is appended after the last worksheet, because a bare Add puts a new sheet in front of whatever is active. An existing sheet keeps its visibility, so the Open Log door can hold it open across a bind.

Parameters:

Returns: Worksheet. The resolved or newly created worksheet.


IsDiseaseSheet #

is-disease-sheet

Whether a worksheet carries the disease tag

Signature:

Private Function IsDiseaseSheet(ByVal sh As Worksheet) As Boolean

HiddenNames.Create raises on a sheet it cannot bind, and a sheet with no tag answers an empty string; both read as a plain worksheet.

Parameters:

Returns: Boolean. True when the sheetTag name reads disease.


AppendHeadingTo #

append-heading-to

The first block of the report: which workbook, when, on what

Signature:

Private Sub AppendHeadingTo(ByVal lines As BetterArray, ByVal sh As Worksheet)

A master setup has no Metadata worksheet, so the header says what a reader of the file wants to know first: the file, the date and the platform of the export, where the file is saved and how many disease worksheets it carries.

Parameters:


AppendEntriesTo #

append-entries-to

Every row of the log sheet, into the report

Signature:

Private Sub AppendEntriesTo(ByVal lines As BetterArray, ByVal sh As Worksheet)

A row carrying the title alone opens a section and reads as a heading in brackets; every other written row is one entry, and it reads as the timestamp, the outcome in brackets, then the action and the detail. Blank rows are dropped, because CheckingOutput lays them out for the eye and they say nothing.

Parameters:


PlainOutcome #

plain-outcome

The outcome word of a row, without the picture in front of it

Signature:

Private Function PlainOutcome(ByVal text As String) As String

Checking paints an outcome with a symbol before the word, and that symbol is what the sheet shows. Print # writes one byte per character, so every one of those symbols reaches the file as a single stray mark. The word alone is what the file wants. A section title carries no symbol and comes back untouched: only a first character outside plain text is taken off.

Parameters:

Returns: String. The word alone.


CellText #

cell-text

One cell as text, with an error value reading as empty

Signature:

Private Function CellText(ByVal cellValue As Variant) As String

A cell holding #N/A or #REF! raises 13 on CStr, and one such cell must keep the export of everything else alive.

Parameters:

Returns: String. The value as text, or an empty string when it is an error.


WriteEntry #

write-entry

Rotate when the cap is passed, then flush one dated entry

Signature:

Private Sub WriteEntry(ByVal action As String, ByVal detail As String, _
                       ByVal scope As Byte, _
                       Optional ByVal source As String = vbNullString)

Parameters:

Throws:


FlushEntry #

flush-entry

Build one Checking bundle and flush it through the writer

Signature:

Private Sub FlushEntry(ByVal action As String, ByVal detail As String, _
                       ByVal scope As Byte, _
                       Optional ByVal source As String = vbNullString)

The bundle title is the section of the action, so the sheet carries three titles. The bundle carries one key, so the key uniqueness of Checking binds inside the bundle and every event stays a plain append.

Parameters:


EntryLabel #

entry-label

Compose the dated label of one entry

Signature:

Private Function EntryLabel(ByVal action As String, ByVal detail As String, _
                            Optional ByVal source As String = vbNullString) As String

The label opens with the timestamp and the platform; the procedure, the action code and the detail follow behind the separator, so the two halves land in their own columns of the log sheet. A separator inside the free text would push it past the last written column, so it is softened to a single dash. The platform is on every line on purpose: a log rotation clears the sheet, so a platform written once at the top of a session would be lost with it.

Parameters:

Returns: String. The composed label.


Soften #

soften

Take the column separator out of a piece of free text

Signature:

Private Function Soften(ByVal text As String) As String

CheckingOutput splits the label on the separator and lays the pieces across three columns, so a separator inside free text would push the rest of the line past the last written column and out of the sheet.

Parameters:

Returns: String. The text with every separator turned into a single dash.


PlatformTag #

platform-tag

The operating system, the bitness and the Excel version, on one tag

Signature:

Private Function PlatformTag() As String

A log read months later has to say where it was written, since the same code behaves differently on the two platforms. The name and the bitness come from the compile constants; the Excel version comes from the application and is read once, because it is the same for every line of a session.

Returns: String. Something of the shape "win-64 excel-16.0".


RotateWhenPastCap #

rotate-when-past-cap

Clear the sheet and restart the log once the cap is passed

Signature:

Private Sub RotateWhenPastCap()

Reads the last written row of the output column. Past the cap, the sheet is cleared and the row marker removed, so CheckingOutput re-initialises on the next render, and the fresh log opens with one info line.


Writer #

writer

Lazily create and return the CheckingOutput writer

Signature:

Private Function Writer() As CheckingOutput

The writer is asked for compact appending, which is the mode a log wants: a section title is written the first time the sheet carries it and never again, and no blank rows stand between the entries of one block.

Returns: CheckingOutput. Writer bound to the log worksheet.


HiddenStore #

hidden-store

Lazily create and return the HiddenNames store of the log sheet

Signature:

Private Function HiddenStore() As HiddenNames

Returns: HiddenNames. Hidden name store bound to the log worksheet.


ErrorHandling

ThrowError #

throw-error

Raise a ProjectError-coded runtime error

Signature:

Private Sub ThrowError(ByVal errNumb As Long, ByVal errorMessage As String)

Centralised error-raising pattern and the set-once seal.

Parameters:

Throws:


Seal #

seal

Seal the instance so setup setters can no longer be written

Signature:

Public Sub Seal()

GuardNotSealed #

guard-not-sealed

Guard a setup setter against writes after sealing

Signature:

Private Sub GuardNotSealed(ByVal propName As String)

Parameters:


Used in (7 file(s))