diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 79e288f..fc7dcb2 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -1,10 +1,9 @@
name: CI
on:
- push:
- branches: [main]
pull_request:
- branches: [main]
+ branches:
+ - main
jobs:
build-and-test:
@@ -26,4 +25,4 @@ jobs:
run: npm run build
- name: Run tests (local Node.js + mocks)
- run: npm test
\ No newline at end of file
+ run: npm test
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..314dd3c
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "typescript.preferences.tsconfigPath": "tsconfig.test.json"
+}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f52f61..2a06a9b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,15 +5,22 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
---
+## [2.1.0] – 2025-06-26
-## [Unreleased]
+### Added
+- **`ExcelAppender` color map support**: The `getInstance` method now accepts a color map, allowing users to specify custom font colors for different log levels. The default color map is exposed as a public property, enabling users to reference or modify it for their own configurations.
+- **Additional documentation**: Added `git-basics.md`, providing concise, project-specific instructions and best practices for using Git effectively within this repository.
+
+### Changed
+- Ensured cross-platform compatibility: Adjustments were made to guarantee the framework works in both Node.js/TypeScript and Office Scripts environments. To pass all tests, `setTimeout` was used in some cases to handle the asynchronous nature of Office Scripts.
+- **VSCode configuration improvements**: The project now uses a dedicated `types` folder to include all `*.d.ts` files, such as Office Scripts declarations and global variable definitions. This streamlines type management and ensures accurate IntelliSense and type checking within VSCode.
---
## [2.0.0] – 2025-06-19
### Added
-- **AssertionError**: Introduced a new error class for clearer assertion failure reporting in the unit test framework (`unit-test-framework.ts`).
+- **`AssertionError**`:** Introduced a new error class for clearer assertion failure reporting in the unit test framework (`unit-test-framework.ts`).
- **Assert enhancements**: Added new convenience methods to the `Assert` class, making test writing more robust and expressive (`unit-test-framework.ts`).
- **New interfaces and types**: Introduced `Logger`, `Layout`, and `LogEvent` interfaces, as well as the `LogEventFactory` type, to enhance extensibility and type safety (`logger.ts`).
- **New classes**: Added `LoggerImpl`, `Utility`, `LayoutImpl`, `LogEventImpl` (`logger.ts`), and `AssertionError` (`unit-test-framework.ts`) to provide a more modular, extensible, and testable architecture.
diff --git a/README.md b/README.md
index c23e067..08e4427 100644
--- a/README.md
+++ b/README.md
@@ -38,13 +38,13 @@ Logger.addAppender(ConsoleAppender.getInstance()) // Add console appender
## Basic Usage Examples
```typescript
-Logger.info("Script started") // [INFO] Script started.
-Logger.warn("This might be a problem") // [WARN] This might be a problem.
-Logger.error("A fatal error occurred") // [ERROR] A fatal error occurred
-Logger.trace("Step-by-step details") // [TRACE] Step-by-step details
+Logger.info("Script started") // [2025-06-25 23:41:10,585] [INFO] Script started.
+Logger.warn("This might be a problem") // [2025-06-25 23:41:10,585] [WARN] This might be a problem.
+Logger.error("A fatal error occurred") // [2025-06-25 23:41:10,585] [ERROR] A fatal error occurred
+Logger.trace("Step-by-step details") // [2025-06-25 23:41:10,585] [TRACE] Step-by-step details
```
-> Output as shown above uses the short layout. With the default layout, a timestamp and brackets are included.
+> Output as shown above uses the default layout. With the short layout, a timestamp and brackets are excluded.
### Logging to Excel Cell
@@ -57,8 +57,8 @@ function main(workbook: ExcelScript.Workbook) {
Logger.getInstance(Logger.LEVEL.INFO, Logger.ACTION.CONTINUE)
Logger.addAppender(ExcelAppender.getInstance(cell))
- Logger.info("Log written to Excel!") // Output in cell B1: [INFO] Log written to Excel! (green text)
- Logger.trace("Trace event in cell") // Output in cell B1: [TRACE] Trace event in cell (gray text)
+ Logger.info("Log written to Excel!") // Output in cell B1: [2025-06-25 23:41:10,586] [INFO] Log written to Excel! (green text)
+ Logger.trace("Trace event in cell") // Output in cell B1: [2025-06-25 23:41:10,586] [TRACE] Trace event in cell (gray text)
}
```
@@ -222,6 +222,7 @@ You can pass an object with arbitrary key-value pairs as the `extrafields` argum
#### Example: Adding custom fields to a log entry
+Following examples assumed a short layout configuration.
```typescript
Logger.info("Processing started", { step: "init", user: "alice@example.com" })
````
@@ -297,10 +298,10 @@ function main(workbook: ExcelScript.Workbook) {
Logger.addAppender(ExcelAppender.getInstance(logCell))
// Logging (with short layout, output shown as comments):
- Logger.info("Script started.") // [INFO] Script started.
+ Logger.info("Script started.") // [INFO] Script started.
Logger.trace("This is a trace message.") // [TRACE] This is a trace message.
- Logger.warn("This is a warning.") // [WARN] This is a warning.
- Logger.error("This is an error!") // [ERROR] This is an error! (if ACTION.EXIT, aborts script)
+ Logger.warn("This is a warning.") // [WARN] This is a warning.
+ Logger.error("This is an error!") // [ERROR] This is an error! (if ACTION.EXIT, aborts script)
// ExcelAppender outputs in cell C2:
// [INFO] Script started. (green text)
@@ -323,6 +324,28 @@ This framework is designed so that the log message layout and log event factory
- Passing these configurations via `getInstance` would require adding extra rarely-used parameters to already overloaded constructors, making the API harder for most users.
- If your scenario truly requires dynamic, runtime reconfiguration, you can fork the codebase or adapt it for your specific needs, but for most Office Scripts, stability is preferred.
+## Cross-Platform Compatibility
+
+This framework is designed to work seamlessly in both Node.js/TypeScript environments (such as VSCode) and directly within Office Scripts.
+
+- **Tested Environments:**
+ - Node.js/TypeScript (VSCode)
+ - Office Scripts (Excel on the web)
+
+- **Usage in Office Scripts:**
+ To use the framework in Office Scripts, paste the source files into your script in the following order:
+ 1. `test/unit-test-framework.ts`
+ 2. `src/logger.ts`
+ 3. `test/main.ts`
+ The order matters because Office Scripts requires that all objects and functions are declared before they are used.
+
+- **Office Scripts Compatibility Adjustments:**
+ - The code avoids unsupported keywords such as `any`, `export`, and `import`.
+ - Office Scripts does not allow calling `ExcelScript` API methods on Office objects inside class constructors; the code is structured to comply with this limitation.
+ - Additional nuances and workarounds are documented in the source code comments.
+
+This ensures the logging framework is robust and reliable across both development and production Office Scripts scenarios.
+
---
## Troubleshooting & FAQ
@@ -348,6 +371,26 @@ This framework is designed so that the log message layout and log event factory
- **Why can't I send a different message to different appenders?**
By design, all channels (appenders) receive the same log event message for consistency.
+- **Why am I getting unexpected results when running some tests in Office Scripts compared to Node.js/TypeScript?**
+ This can happen because Office Scripts executes code asynchronously, which means some operations (like logging or cell updates) may not complete in strict sequence. As a result, test outcomes may differ from those in Node.js/TypeScript, which runs synchronously and flushes operations immediately.
+
+ **Workaround:**
+ To ensure reliable test results in Office Scripts, introduce a delay or use asynchronous test helpers to wait for operations to complete before making assertions. In `test/main.ts`, the `TestCase.runTestAsync` method is used for this purpose. For example:
+
+ ```typescript
+ let actualEvent = appender.getLastLogEvent()
+ TestCase.runTestAsync(() => {
+ Assert.isNotNull(actualEvent, "ExcelAppender(getLastLogEvent) not null")
+ Assert.equals(actualEvent!.type, expectedType, "ExcelAppender(getLastLogEvent).type")
+ Assert.equals(actualEvent!.message, expectedMsg, "ExcelAppender(getLastLogEvent).message")
+ // Now checking the Excel cell value (formatted via layout)
+ let expectedStr = AbstractAppender.getLayout().format(actualEvent as LogEvent)
+ Assert.equals(actualStr, expectedStr, "ExcelAppender(cell value via log(string,LOG_EVENT))")
+ })
+ ```
+
+ By wrapping assertions in `runTestAsync`, you allow asynchronous operations to finish, making your tests more reliable across both environments.
+
---
## Additional Information
diff --git a/docs/git-basics.md b/docs/git-basics.md
new file mode 100644
index 0000000..f66cd75
--- /dev/null
+++ b/docs/git-basics.md
@@ -0,0 +1,94 @@
+# Git Basic Operations Reference
+
+## 1. Setup
+
+```bash
+git config --global user.name "Your Name"
+git config --global user.email "you@example.com"
+```
+
+## 2. Creating a Repository
+
+```bash
+git init # Initialize new repo in current directory
+git clone
Appenders such as 'ConsoleAppender' and 'ExcelAppender' should extend this class to inherit consistent logging behavior and event creation via the LogEventFactory.
-ProtectedconstructorProtectedclearTo ensure when invoking clearInstance in a sub-class it also clear the last log event
-The last log event sent by the appender.
-Log a message or event.
+ProtectedclearTo ensure when invoking clearInstance in a sub-class it also clear the last log event
+The last log event sent by the appender.
+Log a message or event.
LogEvent or message string.
Optionalarg2: LOG_EVENTLOG_EVENT, only required if arg1 is a string.
Optionalarg3: LogEventExtraFieldsextraFields, only used if arg1 is a string.
-Protected AbstractsendSend the event to the appropriate destination. The event is stored based on the
+Protected AbstractsendSend the event to the appropriate destination. The event is stored based on the
The log event to be sent.
Optionalcontext: string(Optional) A string to provide additional context in case of an error.
ProtectedsetSend the event to the appropriate destination.
+ProtectedsetSend the event to the appropriate destination.
The log event to be sent.
Returns a string representation of the appender. +
Returns a string representation of the appender. It includes the information from the base class plus the information of the current class, so far this class doesn't have additional properties to show.
A string representation of the appender.
-StaticclearSets to null the static layout, useful for running different scenarios.
-StaticclearSets to null the log event factory, useful for running different scenarios.
-StaticgetThe layout associated to all events. Used to format the log event before sending it to the appenders. +
StaticclearSets to null the static layout, useful for running different scenarios.
+StaticclearSets to null the log event factory, useful for running different scenarios.
+StaticgetThe layout associated to all events. Used to format the log event before sending it to the appenders. If the layout was not set, it returns a default layout (lazy initialization). The layout is shared by all events and all appenders, so it is static.
StaticgetGets the log event factory function used to create LogEvent instances. If it was not set before, it returns the default factory function.
+StaticgetGets the log event factory function used to create LogEvent instances. If it was not set before, it returns the default factory function.
The log event factory function.
-StaticsetSets the layout associated to all events, the layout is assigned only if it was not set before.
+StaticsetSets the layout associated to all events, the layout is assigned only if it was not set before.
The layout to set.
StaticsetSets the log event factory function used to create LogEvent instances if it was not set before.
+StaticsetSets the log event factory function used to create LogEvent instances if it was not set before.
A factory function to create LogEvent instances. Must have the signature (message: string, eventType: LOG_EVENT) => LogEvent. If not provided, a default factory function is used.
@@ -62,4 +63,4 @@// Example: Custom LogEvent to be used to specify the environment where the log event was created.
let prodLogEventFactory: LogEventFactory
= function prodLogEventFactoryFun(message: string, eventType: LOG_EVENT) {
return new LogEventImpl("PROD-" + message, eventType) // add environment prefix
}
AbstractAppender.setLogEventFactory(prodLogEventFactory) // Now all appenders will use ProdLogEvent
-
Constructs a new AbstractAppender instance. Nothing is initialized, because the class only has static properties that are lazy initialized or set by the user.
-