# Log Driven Development

## Primary goal:
Получить свазнный AI Firendly логирование по которому мождно было воставнить что происходило с системой в виде некого аналогка call graph, чтобы получить какие методы вызывались, какие аргументы и результат.


## Example: OOP
```ts
import { LDD } from '#logger/ldd';

/** @purpose Creates URL-friendly slugs from strings. */
@LDD.class('Slugify')
export class Slugify {
  /**
   * @purpose Converts a string into a URL-friendly slug.
   * @param text The input string to convert.
   * @param options An optional object with configuration.
   * @returns A promise that resolves to the generated slug.
   * @throws {TessellError} Throws if the input `text` is not a non-empty string.
   * @invariant The output will only contain lowercase alphanumeric characters and the delimiter.
   */
  @LDD.method()
  async create(text: string, options?: { delimiter?: string }): Promise<string> {
    // START_CREATE_VALIDATE_INPUT
    if (typeof text !== 'string' || text.length === 0) {
      throw LDD.error({
        state: `validate`,
        error: new Error('Input text must be a non-empty string.', { cause: { text } }),
      });
    }
    // END_CREATE_VALIDATE_INPUT

    const delimiter = options?.delimiter ?? '-';

    LDD.debug({state: 'transform'}).msg`Apply '${delimiter}' delimiter`;

    // START_CREATE_APPLY_TRANSFORMATIONS
    const slug = text
      .toLowerCase()
      .replace(/\s+/g, delimiter)
      .replace(new RegExp(`[^a-z0-9${delimiter}]`, 'g'), '');
    // END_CREATE_APPLY_TRANSFORMATIONS

    return slug;
  }
}

const slugify = new Slugify();
// LOG: [datetime] [debug] [Slugify#constructor] Instantiated {tid: <traceId>, args: <arguments>, time: <ms>}

const result = await slugify.create() ;
// LOG: [datetime] [debug] [Slugify#create] [idle → invoked] {tid: <traceId>, args: <redacted arguments>}
// LOG: [datetime] [error] [Slugify#create] [invoked → validate] {tid: <traceId>, error, time: <ms>}

const result = await slugify.create('...') ;
// LOG: [datetime] [debug] [Slugify#create] [idle → invoked] {tid: <traceId>, args: <redacted arguments>}
// LOG: [datetime] [debug] [Slugify#create] [invoked → transform] Apply '-' delimiter {tid: <traceId>, time: <ms>}
// LOG: [datetime] [debug] [Slugify#create] [transform → returns] {tid: <traceId>, returns: <redacted value>, time: <ms>}
```

### Example: Functional
```ts
import { LDD } from '#logger/ldd';

/** @purpose Конфигурация для выбора стратегии обработки. */
export type DecideStrategyConfig = {
  /** @purpose Путь к целевому ресурсу (если отсутствует, используется fallback). */
  path?: string;
};

/**
 * @purpose Определяет стратегию обработки на основе наличия пути в конфигурации.
 * @param config Параметры конфигурации.
 * @returns Идентификатор выбранной стратегии.
 */
@LDD.fn('decideStrategy')
export function decideStrategy(config: DecideStrategyConfig): string {
  if (!config?.path) {
    return LDD.warn({
      state: 'fallback',
      returnValue: './',
    }).msg`No 'config.path' provided`;
  }

  return 'primary';
}

decideStrategy();
// LOG: [datetime] [debug] [decideStrategy] [idle → invoked] {tid: <traceId>, args: <redacted arguments>}
// LOG: [datetime] [debug] [decideStrategy] [invoked → fallback] No 'config.path' provided {tid: <traceId>, returns: <redacted returnValue>}
```

## API

### `@LDD.class`
ЦЕЛЬ: Добавить в класс свойсиво `LDD_SYMBOL` (если его нет) равный экземпляру `LDD.Logger(<name>, LDD.active)`, так же модифицировать `constructor` (чтобы добавить логгирование), по факту выпонить LDD.method для него.

### `@LDD.method`
Обернуть метод класса в try catch и выставлять сохраняет текущий `LDD.active` и выставляет новый на время работы метода.

### `@LDD.fn`
Аналогично `LDD.method`, но для произвольной функции.

### `LOG.debug/info/warn/error`
Методы логирования которые формируют лог на основе `LDD.active`

### `LDD.active`
Активный контекст логирования, Он должен участвовать не только в выводе лога, но и добавляться в контекст, который структурный выводится вторым аргументом влоги. И добавлять туда... Добавлять туда индификатор с связанной записи. Это должен быть TID. 

ВАЖНО: Также, если у нас происходит варнинг или ошибка, то должно сразу же выводиться как бы под логом еще записи в виде колтрейса. Вот как раз под цепочки TID нужно поднять всю цепочку записи, чтобы можно было показать цепочку вызовов.


**Active Moni
```
app:
  stats:
    - CPU: 10% (current), 5% (avg), 3% (min), 50% (max)
    - Working Memory: 800M (current), 500M (avg), 200M (min), 800M (max)
    - <другие метрики, например Peak Working Memory>
```