Repository: hyperjumptech/monika
Pull request #1277 (2024-04-18 09:40:35)

Refactor: Separate flag handlers
# Monika Pull Request (PR)

## What feature/issue does this PR add

To ease the transition to multi-command (#1154). These changes extracted the log, flush, and summary flag handlers into its own file.

## How to test

- Run `npm start`
- Run `npm start -- --summary`
- Run `npm start -- --logs`
- Run `npm start -- --flush --force`
- Run `npm test`


## Screenshot
<img width="1726" alt="Screenshot 2024-04-18 at 4 37 15 PM" src="https://github.com/hyperjumptech/monika/assets/15191978/4be18dfd-60ce-4f12-9ad8-2f8fcc77088d">



Patch:
diff --git a/src/commands/monika.ts b/src/commands/monika.ts
index a653a2a0..0f6f4844 100644
--- a/src/commands/monika.ts
+++ b/src/commands/monika.ts
@@ -22,7 +22,7 @@
  * SOFTWARE.                                                                      *
  **********************************************************************************/
 
-import { Command, Errors, Flags } from '@oclif/core'
+import { Command, Errors } from '@oclif/core'
 import pEvent from 'p-event'
 
 import type { Config } from '../interfaces/config'
@@ -33,38 +33,27 @@ import { createConfig } from '../components/config/create'
 import { sortProbes } from '../components/config/sort'
 import { printAllLogs } from '../components/logger'
 import { flush } from '../components/logger/flush'
-import { closeLog, openLogfile } from '../components/logger/history'
+import { closeLog } from '../components/logger/history'
 import { logStartupMessage } from '../components/logger/startup-message'
 import { scheduleSummaryNotification } from '../components/notification/schedule-notification'
 import { sendMonikaStartMessage } from '../components/notification/start-message'
+import { printSummary } from '../components/summary'
 import { getContext, setContext } from '../context'
 import events from '../events'
-import {
-  type MonikaFlags,
-  monikaFlagsDefaultValue,
-  retryInitialDelayMs,
-  retryMaxDelayMs,
-  symonAPIVersion,
-  symonGetProbesIntervalMs,
-} from '../flag'
-import { printSummary, savePidFile } from '../jobs/summary-notification'
+import { type MonikaFlags, sanitizeFlags, flags } from '../flag'
+import { savePidFile } from '../jobs/summary-notification'
 import initLoaders from '../loaders'
 import { sanitizeProbe, startProbing } from '../looper'
 import SymonClient from '../symon'
 import { getEventEmitter } from '../utils/events'
 import { log } from '../utils/pino'
-
-type GetProbesParams = {
-  config: Config
-  flags: MonikaFlags
-}
+import { fetchAndCacheNetworkInfo } from '../utils/public-ip'
 
 const em = getEventEmitter()
 let symonClient: SymonClient
 
 export default class Monika extends Command {
   static description = 'Monika command line monitoring tool'
-
   static examples = [
     'monika',
     'monika --logs',
@@ -73,208 +62,7 @@ export default class Monika extends Command {
     'monika --config https://raw.githubusercontent.com/hyperjumptech/monika/main/monika.example.yml --config-interval 900',
   ]
 
-  static flags = {
-    'auto-update': Flags.string({
-      description:
-        'Enable auto-update for Monika. Available options: major, minor, patch. This will make Monika terminate itself on successful update but does not restart',
-    }),
-    config: Flags.string({
-      char: 'c',
-      default: monikaFlagsDefaultValue.config,
-      description:
-        'JSON configuration filename or URL. If none is supplied, will look for monika.yml in the current directory',
-      env: 'MONIKA_JSON_CONFIG',
-      multiple: true,
-    }),
-
-    'config-filename': Flags.string({
-      default: monikaFlagsDefaultValue['config-filename'],
-      dependsOn: ['config'],
-      description:
-        'The configuration filename for config file created if there is no config file found ',
-    }),
-
-    'config-interval': Flags.integer({
-      default: monikaFlagsDefaultValue['config-interval'],
-      dependsOn: ['config'],
-      description:
-        'The interval (in seconds) for periodic config checking if url is used as config source',
-    }),
-
-    'create-config': Flags.boolean({
-      description:
-        'Create config from HAR (-H), postman (-p), insomnia (-I), sitemap (--sitemap), textfile (--text) export file, or open Monika Configuration Generator using default browser',
-    }),
-
-    'native-fetch': Flags.boolean({
-      default: monikaFlagsDefaultValue['native-fetch'],
-      description:
-        'Use native fetch Node.js API instead of Axios for HTTP client',
-    }),
-
-    flush: Flags.boolean({
-      description: 'Flush logs',
-    }),
-
-    'follow-redirects': Flags.integer({
-      default: monikaFlagsDefaultValue['follow-redirects'],
-      description:
-        'Monika will follow redirects as many times as the specified value here. By default, Monika will follow redirects once. To disable redirects following, set the value to zero.',
-    }),
-
-    force: Flags.boolean({
-      default: false,
-      description: 'Force commands with a yes whenever Y/n is prompted.',
-    }),
-
-    har: Flags.string({
-      char: 'H', // (H)ar file to
-      description: 'Run Monika using a HAR file',
-      exclusive: ['postman', 'insomnia', 'sitemap', 'text'],
-      multiple: false,
-    }),
-
-    help: Flags.help({ char: 'h' }),
-
-    id: Flags.string({
-      char: 'i', // (i)ds to run
-      description: 'Define specific probe ids to run',
-      multiple: false,
-    }),
-
-    ignoreInvalidTLS: Flags.boolean({
-      description:
-        'Configures whether HTTPS requests should ignore invalid certificates',
-      default: false,
-    }),
-
-    insomnia: Flags.string({
-      char: 'I', // (I)nsomnia file to
-      description: 'Run Monika using an Insomnia json/yaml file',
-      exclusive: ['har', 'postman', 'sitemap', 'text'],
-      multiple: false,
-    }),
-
-    'keep-verbose-logs': Flags.boolean({
-      default: false,
-      description: 'Store all requests logs to database',
-    }),
-
-    logs: Flags.boolean({
-      char: 'l', // prints the (l)ogs
-      description: 'Print all logs.',
-    }),
-
-    'one-probe': Flags.boolean({
-      dependsOn: ['sitemap'],
-      description: 'One Probe',
-    }),
-
-    output: Flags.string({
-      char: 'o', // (o)utput file to write config to
-      description: 'Write monika config file to this file',
-      multiple: false,
-    }),
-
-    postman: Flags.string({
-      char: 'p', // (p)ostman
-      description: 'Run Monika using a Postman json file.',
-      exclusive: ['har', 'insomnia', 'sitemap', 'text'],
-      multiple: false,
-    }),
-
-    prometheus: Flags.integer({
-      description:
-        'Specifies the port the Prometheus metric server is listening on. e.g., 3001. (EXPERIMENTAL)',
-      exclusive: ['r'],
-    }),
-
-    repeat: Flags.integer({
-      char: 'r', // (r)epeat
-      default: 0,
-      description: 'Repeats the test run n times',
-      multiple: false,
-    }),
-
-    retryInitialDelayMs,
-
-    retryMaxDelayMs,
-
-    sitemap: Flags.string({
-      description: 'Run Monika using a Sitemap xml file.',
-      exclusive: ['har', 'insomnia', 'postman', 'text'],
-      multiple: false,
-    }),
-
-    'status-notification': Flags.string({
-      description: 'Cron syntax for status notification schedule',
-    }),
-
-    stun: Flags.integer({
-      char: 's', // (s)stun
-      default: monikaFlagsDefaultValue.stun,
-      description: 'Interval in seconds to check STUN server',
-      multiple: false,
-    }),
-
-    summary: Flags.boolean({
-      default: false,
-      description: 'Display a summary of monika running stats',
-    }),
-
-    'symon-api-version': symonAPIVersion(),
-
-    symonKey: Flags.string({
-      dependsOn: ['symonUrl'],
-      description: 'API Key for Symon',
-    }),
-
-    symonGetProbesIntervalMs,
-
-    symonLocationId: Flags.string({
-      dependsOn: ['symonKey', 'symonUrl'],
-      description: 'Location ID for Symon (optional)',
-      required: false,
-    }),
-
-    symonMonikaId: Flags.string({
-      dependsOn: ['symonKey', 'symonUrl'],
-      description: 'Monika ID for Symon (optional)',
-      required: false,
-    }),
-
-    symonReportInterval: Flags.integer({
-      dependsOn: ['symonKey', 'symonUrl'],
-      description: 'Interval for reporting to Symon in milliseconds (optional)',
-      required: false,
-    }),
-
-    symonReportLimit: Flags.integer({
-      dependsOn: ['symonKey', 'symonUrl'],
-      description: 'Data limit to be reported to Symon (optional)',
-      required: false,
-    }),
-
-    symonUrl: Flags.string({
-      dependsOn: ['symonKey'],
-      description: 'URL of Symon',
-      hidden: false,
-    }),
-
-    text: Flags.string({
-      description: 'Run Monika using a Simple text file',
-      exclusive: ['postman', 'insomnia', 'sitemap', 'har'],
-      multiple: false,
-    }),
-
-    verbose: Flags.boolean({
-      default: false,
-      description: 'Show verbose log messages',
-    }),
-
-    version: Flags.version({ char: 'v' }),
-  }
-
+  static flags = flags
   static id = 'monika'
 
   async catch(error: Error): Promise<unknown> {
@@ -298,113 +86,63 @@ export default class Monika extends Command {
     throw error
   }
 
-  deprecationHandler(config: Config): Config {
-    const showDeprecateMsg: Record<'query', boolean> = {
-      query: false,
-    }
-
-    const checkedConfig = {
-      ...config,
-      probes: config.probes?.map((probe) => ({
-        ...probe,
-        requests: probe.requests?.map((request) => ({
-          ...request,
-          alert: request.alerts?.map((alert) => {
-            if (alert.query) {
-              showDeprecateMsg.query = true
-              return { ...alert, assertion: alert.query }
-            }
-
-            return alert
-          }),
-        })),
-        alerts: probe.alerts?.map((alert) => {
-          if (alert.query) {
-            showDeprecateMsg.query = true
-            return { ...alert, assertion: alert.query }
-          }
-
-          return alert
-        }),
-      })),
-    }
-
-    if (showDeprecateMsg.query) {
-      log.warn('"alerts.query" is deprecated. Please use "alerts.assertion"')
-    }
-
-    return checkedConfig
-  }
-
-  getProbes({ config, flags }: GetProbesParams): Probe[] {
-    const sortedProbes = sortProbes(config.probes, flags.id)
-
-    return sortedProbes.map((probe: Probe) =>
-      sanitizeProbe(isSymonModeFrom(flags), probe)
-    )
-  }
-
   async run(): Promise<void> {
-    const monika = await this.parse(Monika)
-    const _flags: MonikaFlags = monika.flags
-    setContext({ flags: _flags })
+    const cmd = await this.parse(Monika)
+    const flags = sanitizeFlags(cmd.flags)
+
+    setContext({ flags, userAgent: this.config.userAgent })
 
     try {
-      if (_flags['create-config']) {
+      if (flags['create-config']) {
         await createConfig()
         return
       }
 
-      await openLogfile()
-
-      if (_flags.logs) {
+      if (flags.logs) {
         await printAllLogs()
-        await closeLog()
         return
       }
 
-      if (_flags.flush) {
-        await flush(_flags.force)
-        await closeLog()
+      if (flags.flush) {
+        await flush()
         return
       }
 
-      if (_flags.summary) {
-        printSummary(this.config)
-        await closeLog()
+      if (flags.summary) {
+        await printSummary()
         return
       }
 
-      await initLoaders(_flags, this.config)
+      await initLoaders(flags, this.config)
+
+      const isSymonMode = isSymonModeFrom(flags)
+      await logRunningInfo({ isSymonMode, isVerbose: flags.verbose })
 
-      if (isSymonModeFrom(_flags)) {
-        symonClient = new SymonClient(_flags)
+      if (isSymonMode) {
+        symonClient = new SymonClient(flags)
         await symonClient.initiate()
       }
 
       let isFirstRun = true
 
       for (;;) {
-        const controller = new AbortController()
-        const { signal } = controller
         const config = getConfig()
-        const notifications = config.notifications || []
-        const probes = this.getProbes({ config, flags: _flags })
+        const probes = getProbes({ config, flags })
 
         // emit the sanitized probe
         em.emit(events.config.sanitized, probes)
-
         // save some data into files for later
-        savePidFile(_flags.config, config)
-
-        this.deprecationHandler(config)
-
+        savePidFile(flags.config, config)
+        deprecationHandler(config)
         logStartupMessage({
           config,
-          flags: _flags,
+          flags,
           isFirstRun,
         })
 
+        const controller = new AbortController()
+        const { signal } = controller
+        const notifications = config.notifications || []
         startProbing({
           notifications,
           probes,
@@ -418,9 +156,8 @@ export default class Monika extends Command {
         sendMonikaStartMessage(notifications).catch((error) =>
           log.error(error.message)
         )
-
         // schedule status update notification
-        scheduleSummaryNotification({ config, flags: _flags })
+        scheduleSummaryNotification({ config, flags })
 
         isFirstRun = false
 
@@ -430,23 +167,93 @@ export default class Monika extends Command {
         controller.abort('Monika configuration updated')
       }
     } catch (error: unknown) {
-      await closeLog()
       this.error((error as Error)?.message, { exit: 1 })
+    } finally {
+      await closeLog()
     }
   }
 }
 
+type RunningInfoParams = { isSymonMode: boolean; isVerbose: boolean }
+
+async function logRunningInfo({ isVerbose, isSymonMode }: RunningInfoParams) {
+  if (!isVerbose && !isSymonMode) {
+    log.info('Monika is running.')
+    return
+  }
+
+  try {
+    const { city, hostname, isp, privateIp, publicIp } =
+      await fetchAndCacheNetworkInfo()
+
+    log.info(
+      `Monika is running from: ${city} - ${isp} (${publicIp}) - ${hostname} (${privateIp})`
+    )
+  } catch (error) {
+    log.warn(`Failed to obtain location/ISP info. Got: ${error}`)
+  }
+}
+
+type GetProbesParams = {
+  config: Config
+  flags: MonikaFlags
+}
+
+function getProbes({ config, flags }: GetProbesParams): Probe[] {
+  const sortedProbes = sortProbes(config.probes, flags.id)
+
+  return sortedProbes.map((probe: Probe) =>
+    sanitizeProbe(isSymonModeFrom(flags), probe)
+  )
+}
+
+function deprecationHandler(config: Config): Config {
+  const showDeprecateMsg: Record<'query', boolean> = {
+    query: false,
+  }
+
+  const checkedConfig = {
+    ...config,
+    probes: config.probes?.map((probe) => ({
+      ...probe,
+      requests: probe.requests?.map((request) => ({
+        ...request,
+        alert: request.alerts?.map((alert) => {
+          if (alert.query) {
+            showDeprecateMsg.query = true
+            return { ...alert, assertion: alert.query }
+          }
+
+          return alert
+        }),
+      })),
+      alerts: probe.alerts?.map((alert) => {
+        if (alert.query) {
+          showDeprecateMsg.query = true
+          return { ...alert, assertion: alert.query }
+        }
+
+        return alert
+      }),
+    })),
+  }
+
+  if (showDeprecateMsg.query) {
+    log.warn('"alerts.query" is deprecated. Please use "alerts.assertion"')
+  }
+
+  return checkedConfig
+}
+
 /**
  * Show Exit Message
  */
 process.on('SIGINT', async () => {
-  if (!process.env.DISABLE_EXIT_MESSAGE) {
-    log.info('Thank you for using Monika!')
-    log.info('We need your help to make Monika better.')
-    log.info(
-      'Can you give us some feedback by clicking this link https://github.com/hyperjumptech/monika/discussions?\n'
-    )
-  }
+  log.info('Thank you for using Monika!')
+  log.info('We need your help to make Monika better.')
+  log.info(
+    'Can you give us some feedback by clicking this link https://github.com/hyperjumptech/monika/discussions?\n'
+  )
 
   if (symonClient) {
     await symonClient.sendStatus({ isOnline: false })
diff --git a/src/components/config/index.ts b/src/components/config/index.ts
index 7d9a8c7a..b1fea4cd 100644
--- a/src/components/config/index.ts
+++ b/src/components/config/index.ts
@@ -28,7 +28,6 @@ import isUrl from 'is-url'
 import events from '../../events'
 import type { Config } from '../../interfaces/config'
 import { getContext, setContext } from '../../context'
-import { monikaFlagsDefaultValue } from '../../flag'
 import type { MonikaFlags } from '../../flag'
 import { getEventEmitter } from '../../utils/events'
 import { md5Hash } from '../../utils/hash'
@@ -146,9 +145,7 @@ async function watchConfigsChange(flags: MonikaFlags) {
     flags.config.map((source, index) =>
       watchConfigChange({
         flags,
-        interval:
-          flags['config-interval'] ||
-          monikaFlagsDefaultValue['config-interval'],
+        interval: flags['config-interval'],
         source,
         type: 'monika',
         index,
diff --git a/src/components/logger/flush.ts b/src/components/logger/flush.ts
index 68d68b20..3338ee03 100644
--- a/src/components/logger/flush.ts
+++ b/src/components/logger/flush.ts
@@ -1,25 +1,22 @@
 import { ux } from '@oclif/core'
-import { flushAllLogs } from './history'
+import { getContext } from '../../context'
 import { log } from '../../utils/pino'
+import { flushAllLogs, openLogfile } from './history'
 
-export async function flush(isForce: boolean): Promise<void> {
-  if (isForce) {
-    await flushAllLogs()
-    log.info('Records flushed, thank you.')
+export async function flush(): Promise<void> {
+  if (!getContext().flags.force) {
+    const answer = await ux.ux.prompt(
+      'Are you sure you want to flush all logs in monika-logs.db (Y/n)?'
+    )
 
-    return
-  }
-
-  const ans = await ux.ux.prompt(
-    'Are you sure you want to flush all logs in monika-logs.db (Y/n)?'
-  )
-
-  if (ans === 'Y') {
-    await flushAllLogs()
-    log.info('Records flushed, thank you.')
+    if (answer !== 'Y') {
+      log.info('Cancelled. Thank you.')
 
-    return
+      return
+    }
   }
 
-  log.info('Cancelled. Thank you.')
+  await openLogfile()
+  await flushAllLogs()
+  log.info('Records flushed, thank you.')
 }
diff --git a/src/components/logger/history.ts b/src/components/logger/history.ts
index cfe9eee4..3c8e2e14 100644
--- a/src/components/logger/history.ts
+++ b/src/components/logger/history.ts
@@ -30,7 +30,6 @@ import { ProbeRequestResponse } from '../../interfaces/request'
 import { Probe } from '../../interfaces/probe'
 import type { Notification } from '@hyperjumptech/monika-notification'
 import { log } from '../../utils/pino'
-import { getConfig } from '../config'
 import { getErrorMessage } from '../../utils/catch-error-handler'
 const sqlite3 = verboseSQLite()
 const dbPath = path.resolve(process.cwd(), 'monika-logs.db')
@@ -84,7 +83,6 @@ export type DeleteProbeRes = {
 }
 
 type Summary = {
-  numberOfProbes: number
   numberOfIncidents: number
   numberOfRecoveries: number
   numberOfSentNotifications: number
@@ -558,10 +556,7 @@ export async function getSummary(): Promise<Summary> {
     0
   )
 
-  const config = getConfig()
-
   return {
-    numberOfProbes: config?.probes?.length || 0,
     numberOfIncidents,
     numberOfRecoveries,
     numberOfSentNotifications,
diff --git a/src/components/logger/index.ts b/src/components/logger/index.ts
index c5b66b68..c344edf6 100644
--- a/src/components/logger/index.ts
+++ b/src/components/logger/index.ts
@@ -23,15 +23,35 @@
  **********************************************************************************/
 
 import chalk from 'chalk'
-import { getAllLogs } from './history'
 import { log } from '../../utils/pino'
+import { getAllLogs, openLogfile } from './history'
+
+// printAllLogs dumps the content of monika-logs.db onto the screen
+export async function printAllLogs(): Promise<void> {
+  await openLogfile()
+  const data = await getAllLogs()
+
+  for (const {
+    id,
+    probeId,
+    requestUrl,
+    responseStatus,
+    responseTime,
+  } of data) {
+    log.info(
+      `${id} id: ${probeId} responseCode: ${chalk.keyword(
+        getStatusColor(responseStatus)
+      )(String(responseStatus))} - ${requestUrl}, ${responseTime || '- '}ms`
+    )
+  }
+}
 
 /**
  * getStatusColor colorizes different statusCode
  * @param {any} responseCode is the httpStatus to colorize
  * @returns {string} color code based on chalk: Chalk & { supportsColor: ColorSupport };
  */
-function getStatusColor(responseCode: number) {
+function getStatusColor(responseCode: number): string {
   switch (Math.trunc(responseCode / 100)) {
     case 2: {
       return 'cyan'
@@ -50,21 +70,3 @@ function getStatusColor(responseCode: number) {
 
   return 'white'
 }
-
-/**
- * printAllLogs dumps the content of monika-logs.db onto the screen
- * @returns Promise<void>
- */
-export async function printAllLogs(): Promise<void> {
-  const data = await getAllLogs()
-
-  for (const row of data) {
-    log.info(
-      `${row.id} id: ${row.probeId} responseCode: ${chalk.keyword(
-        getStatusColor(row.responseStatus)
-      )(String(row.responseStatus))} - ${row.requestUrl}, ${
-        row.responseTime || '- '
-      }ms`
-    )
-  }
-}
diff --git a/src/components/summary.ts b/src/components/summary.ts
new file mode 100644
index 00000000..ffdd9c6b
--- /dev/null
+++ b/src/components/summary.ts
@@ -0,0 +1,77 @@
+import { readFile } from 'node:fs/promises'
+import { hostname } from 'node:os'
+import type { Notification } from '@hyperjumptech/monika-notification'
+
+import { getContext } from '../context'
+import type { Probe } from '../interfaces/probe'
+import getIp from '../utils/ip'
+import { log } from '../utils/pino'
+import { publicIpAddress } from '../utils/public-ip'
+import { getSummary, openLogfile } from './logger/history'
+
+// printSummary gathers and print some stats
+export async function printSummary(): Promise<void> {
+  await openLogfile()
+
+  const [pidFileContent, summary] = await Promise.all([
+    readPidFile(),
+    getSummary(),
+  ])
+  const {
+    monikaConfigFile,
+    monikaNotifs,
+    monikaPid,
+    monikaProbes,
+    monikaStartTime,
+  } = pidFileContent
+  const { numberOfIncidents, numberOfRecoveries, numberOfSentNotifications } =
+    summary
+  const uptime = getDaysHours(monikaStartTime)
+  const host = `${hostname()} (${[publicIpAddress, getIp()]
+    .filter(Boolean)
+    .join('/')})`
+
+  log.info(`Monika Summary
+
+Monika process id\t: ${monikaPid}
+Active config file\t: ${monikaConfigFile}
+Probes set\t\t: ${monikaProbes}
+Notifications set\t: ${monikaNotifs}
+Number of incidents\t: ${numberOfIncidents} in last 24hr
+Number of recoveries\t: ${numberOfRecoveries} in last 24hr
+Number of notifications\t: ${numberOfSentNotifications} in last 24h
+
+Up time\t\t: ${uptime}
+Running on\t: ${host}
+App version\t: ${getContext().userAgent}`)
+}
+
+// getDaysHours breaks down the date into days  hours minute string
+function getDaysHours(startTime: Date): string {
+  let duration = Math.abs((Date.now() - new Date(startTime).getTime()) / 1000)
+  const numDays = Math.floor(duration / 86_400)
+  duration -= numDays * 86_400 // get the remaining hours
+
+  const numHours = Math.floor(duration / 3600) % 24
+  duration -= numHours * 3600 // get the remaining minutes
+
+  const numMinutes = Math.floor(duration / 60) % 60
+
+  const numSeconds = Math.floor(duration - numMinutes * 60)
+
+  return `${numDays} days, ${numHours} hours, ${numMinutes} minutes, ${numSeconds} seconds`
+}
+
+type PidObject = {
+  monikaPid: number
+  monikaConfigFile: string
+  monikaStartTime: Date
+  monikaProbes: Probe
+  monikaNotifs: Notification
+}
+// readsPidFile reads a local monika.pid file and returns the information in it
+async function readPidFile(): Promise<PidObject> {
+  const fileContent = await readFile('monika.pid', { encoding: 'utf8' })
+
+  return JSON.parse(fileContent)
+}
diff --git a/src/context/monika-flags.ts b/src/flag.test.ts
similarity index 81%
rename from src/context/monika-flags.ts
rename to src/flag.test.ts
index b3926281..c0173f81 100644
--- a/src/context/monika-flags.ts
+++ b/src/flag.test.ts
@@ -22,16 +22,15 @@
  * SOFTWARE.                                                                      *
  **********************************************************************************/
 
-import fs from 'fs'
-import path from 'path'
+import { expect } from '@oclif/test'
+import { monikaFlagsDefaultValue, sanitizeFlags } from './flag'
 
-export function getDefaultConfig(): Array<string> {
-  const filesArray = fs.readdirSync(path.dirname('../'))
-  const monikaDotJsonFile = filesArray.find((x) => x === 'monika.json')
-  const monikaDotYamlFile = filesArray.find(
-    (x) => x === 'monika.yml' || x === 'monika.yaml'
-  )
-  const defaultConfig = monikaDotYamlFile || monikaDotJsonFile
+describe('Flag', () => {
+  it('should fill flags with the default value', () => {
+    // act
+    const sanitizedFlags = sanitizeFlags({})
 
-  return defaultConfig ? [defaultConfig] : []
-}
+    // assert
+    expect(sanitizedFlags).deep.eq(monikaFlagsDefaultValue)
+  })
+})
diff --git a/src/flag.ts b/src/flag.ts
index e2a2c8ea..d7b37f83 100644
--- a/src/flag.ts
+++ b/src/flag.ts
@@ -22,9 +22,10 @@
  * SOFTWARE.                                                                      *
  **********************************************************************************/
 
-import { Flags } from '@oclif/core'
+import fs from 'node:fs'
+import path from 'node:path'
 
-import { getDefaultConfig } from './context/monika-flags'
+import { Flags } from '@oclif/core'
 
 export enum SYMON_API_VERSION {
   'v1' = 'v1',
@@ -37,18 +38,17 @@ export type MonikaFlags = {
   'config-filename': string
   'config-interval': number
   'create-config': boolean
-  'native-fetch'?: boolean
+  'native-fetch': boolean
   flush: boolean
   'follow-redirects': number
   force: boolean
   har?: string
   id?: string
   insomnia?: string
-  json?: boolean
   'keep-verbose-logs': boolean
   logs: boolean
   'one-probe': boolean
-  output?: string
+  output: string
   postman?: string
   prometheus?: number
   repeat: number
@@ -58,64 +58,231 @@ export type MonikaFlags = {
   'status-notification'?: string
   stun: number
   summary: boolean
-  'symon-api-version'?: SYMON_API_VERSION
+  'symon-api-version': SYMON_API_VERSION
   symonKey?: string
   symonLocationId?: string
   symonMonikaId?: string
-  symonReportInterval?: number
-  symonReportLimit?: number
+  symonReportInterval: number
+  symonReportLimit: number
   symonGetProbesIntervalMs: number
   symonUrl?: string
   text?: string
-  ignoreInvalidTLS?: boolean
+  ignoreInvalidTLS: boolean
   verbose: boolean
-  version: void
 }
 
+const DEFAULT_CONFIG_INTERVAL_SECONDS = 900
+const DEFAULT_SYMON_REPORT_INTERVAL_MS = 10_000
+
 export const monikaFlagsDefaultValue: MonikaFlags = {
   config: getDefaultConfig(),
   'config-filename': 'monika.yml',
-  'config-interval': 900,
+  'config-interval': DEFAULT_CONFIG_INTERVAL_SECONDS,
   'create-config': false,
-  'native-fetch': false,
   flush: false,
   'follow-redirects': 21,
   force: false,
+  ignoreInvalidTLS: false,
   'keep-verbose-logs': false,
   logs: false,
+  'native-fetch': false,
   'one-probe': false,
+  output: 'monika.yml',
   repeat: 0,
   retryInitialDelayMs: 2000,
   retryMaxDelayMs: 30_000,
   // default is 20s interval lookup
   stun: 20,
   summary: false,
-  symonGetProbesIntervalMs: Number.parseInt(
-    process.env.FETCH_PROBES_INTERVAL ?? '60000',
-    10
-  ),
+  'symon-api-version': SYMON_API_VERSION.v1,
+  symonGetProbesIntervalMs: 60_000,
+  symonReportInterval: DEFAULT_SYMON_REPORT_INTERVAL_MS,
+  symonReportLimit: 100,
   verbose: false,
-  version: undefined,
 }
 
-export const symonAPIVersion = Flags.custom<SYMON_API_VERSION>({
-  default: SYMON_API_VERSION.v1,
-  description:
-    'Symon API version to use. Available options: v1, v2. Default: v1',
-  options: [SYMON_API_VERSION.v1, SYMON_API_VERSION.v2],
-})
+function getDefaultConfig(): Array<string> {
+  const filesArray = fs.readdirSync(path.dirname('../'))
+  const monikaDotJsonFile = filesArray.find((x) => x === 'monika.json')
+  const monikaDotYamlFile = filesArray.find(
+    (x) => x === 'monika.yml' || x === 'monika.yaml'
+  )
+  const defaultConfig = monikaDotYamlFile || monikaDotJsonFile
 
-export const symonGetProbesIntervalMs = Flags.integer({
-  default: monikaFlagsDefaultValue.symonGetProbesIntervalMs,
-  description: `To determine how often Monika sends a request to Symon to get probe data, in milliseconds. Defaults to ${monikaFlagsDefaultValue.symonGetProbesIntervalMs}ms`,
-})
+  return defaultConfig ? [defaultConfig] : []
+}
 
-export const retryInitialDelayMs = Flags.integer({
-  default: monikaFlagsDefaultValue.retryInitialDelayMs,
-  description: `The initial, first delay of the backoff retry when probe request is failed, in milliseconds. Defaults to ${monikaFlagsDefaultValue.retryInitialDelayMs}ms`,
-})
+export const flags = {
+  'auto-update': Flags.string({
+    description:
+      'Enable auto-update for Monika. Available options: major, minor, patch. This will make Monika terminate itself on successful update but does not restart',
+  }),
+  config: Flags.string({
+    char: 'c',
+    default: monikaFlagsDefaultValue.config,
+    description:
+      'JSON configuration filename or URL. If none is supplied, will look for monika.yml in the current directory',
+    env: 'MONIKA_JSON_CONFIG',
+    multiple: true,
+  }),
+  'config-filename': Flags.string({
+    default: monikaFlagsDefaultValue['config-filename'],
+    dependsOn: ['config'],
+    description:
+      'The configuration filename for config file created if there is no config file found ',
+  }),
+  'config-interval': Flags.integer({
+    default: monikaFlagsDefaultValue['config-interval'],
+    dependsOn: ['config'],
+    description:
+      'The interval (in seconds) for periodic config checking if url is used as config source',
+  }),
+  'create-config': Flags.boolean({
+    description:
+      'Create config from HAR (-H), postman (-p), insomnia (-I), sitemap (--sitemap), textfile (--text) export file, or open Monika Configuration Generator using default browser',
+    default: monikaFlagsDefaultValue['create-config'],
+  }),
+  flush: Flags.boolean({
+    description: 'Flush logs',
+    default: monikaFlagsDefaultValue.flush,
+  }),
+  'follow-redirects': Flags.integer({
+    default: monikaFlagsDefaultValue['follow-redirects'],
+    description:
+      'Monika will follow redirects as many times as the specified value here. By default, Monika will follow redirects once. To disable redirects following, set the value to zero.',
+  }),
+  force: Flags.boolean({
+    default: monikaFlagsDefaultValue.force,
+    description: 'Force commands with a yes whenever Y/n is prompted.',
+  }),
+  har: Flags.string({
+    char: 'H', // (H)ar file to
+    description: 'Run Monika using a HAR file',
+    exclusive: ['postman', 'insomnia', 'sitemap', 'text'],
+  }),
+  help: Flags.help({ char: 'h' }),
+  id: Flags.string({
+    char: 'i', // (i)ds to run
+    description: 'Define specific probe ids to run',
+  }),
+  ignoreInvalidTLS: Flags.boolean({
+    description:
+      'Configures whether HTTPS requests should ignore invalid certificates',
+    default: monikaFlagsDefaultValue.ignoreInvalidTLS,
+  }),
+  insomnia: Flags.string({
+    char: 'I', // (I)nsomnia file to
+    description: 'Run Monika using an Insomnia json/yaml file',
+    exclusive: ['har', 'postman', 'sitemap', 'text'],
+  }),
+  'keep-verbose-logs': Flags.boolean({
+    default: monikaFlagsDefaultValue['keep-verbose-logs'],
+    description: 'Store all requests logs to database',
+  }),
+  logs: Flags.boolean({
+    char: 'l', // prints the (l)ogs
+    description: 'Print all logs.',
+    default: monikaFlagsDefaultValue.logs,
+  }),
+  'native-fetch': Flags.boolean({
+    default: monikaFlagsDefaultValue['native-fetch'],
+    description:
+      'Use native fetch Node.js API instead of Axios for HTTP client',
+  }),
+  'one-probe': Flags.boolean({
+    dependsOn: ['sitemap'],
+    description: 'One Probe',
+  }),
+  output: Flags.string({
+    char: 'o', // (o)utput file to write config to
+    description: 'Write monika config file to this file',
+    default: monikaFlagsDefaultValue.output,
+  }),
+  postman: Flags.string({
+    char: 'p', // (p)ostman
+    description: 'Run Monika using a Postman json file.',
+    exclusive: ['har', 'insomnia', 'sitemap', 'text'],
+  }),
+  prometheus: Flags.integer({
+    description:
+      'Specifies the port the Prometheus metric server is listening on. e.g., 3001. (EXPERIMENTAL)',
+    exclusive: ['r'],
+  }),
+  repeat: Flags.integer({
+    char: 'r', // (r)epeat
+    default: monikaFlagsDefaultValue.repeat,
+    description: 'Repeats the test run n times',
+  }),
+  retryInitialDelayMs: Flags.integer({
+    default: monikaFlagsDefaultValue.retryInitialDelayMs,
+    description: `The initial, first delay of the backoff retry when probe request is failed, in milliseconds. Defaults to ${monikaFlagsDefaultValue.retryInitialDelayMs}ms`,
+  }),
+  retryMaxDelayMs: Flags.integer({
+    default: monikaFlagsDefaultValue.retryMaxDelayMs,
+    description: `Maximum backoff retry delay, in milliseconds. Defaults to ${monikaFlagsDefaultValue.retryMaxDelayMs}ms.`,
+  }),
+  sitemap: Flags.string({
+    description: 'Run Monika using a Sitemap xml file.',
+    exclusive: ['har', 'insomnia', 'postman', 'text'],
+  }),
+  'status-notification': Flags.string({
+    description: 'Cron syntax for status notification schedule',
+  }),
+  stun: Flags.integer({
+    char: 's', // (s)stun
+    default: monikaFlagsDefaultValue.stun,
+    description: 'Interval in seconds to check STUN server',
+  }),
+  summary: Flags.boolean({
+    default: monikaFlagsDefaultValue.summary,
+    description: 'Display a summary of monika running stats',
+  }),
+  'symon-api-version': Flags.custom<SYMON_API_VERSION>({
+    default: monikaFlagsDefaultValue['symon-api-version'],
+    description:
+      'Symon API version to use. Available options: v1, v2. Default: v1',
+    options: [SYMON_API_VERSION.v1, SYMON_API_VERSION.v2],
+  })(),
+  symonKey: Flags.string({
+    dependsOn: ['symonUrl'],
+    description: 'API Key for Symon',
+  }),
+  symonGetProbesIntervalMs: Flags.integer({
+    default: monikaFlagsDefaultValue.symonGetProbesIntervalMs,
+    description: `To determine how often Monika sends a request to Symon to get probe data, in milliseconds. Defaults to ${monikaFlagsDefaultValue.symonGetProbesIntervalMs}ms`,
+  }),
+  symonLocationId: Flags.string({
+    dependsOn: ['symonKey', 'symonUrl'],
+    description: 'Location ID for Symon (optional)',
+  }),
+  symonMonikaId: Flags.string({
+    dependsOn: ['symonKey', 'symonUrl'],
+    description: 'Monika ID for Symon (optional)',
+  }),
+  symonReportInterval: Flags.integer({
+    dependsOn: ['symonKey', 'symonUrl'],
+    description: 'Interval for reporting to Symon in milliseconds (optional)',
+  }),
+  symonReportLimit: Flags.integer({
+    dependsOn: ['symonKey', 'symonUrl'],
+    description: 'Data limit to be reported to Symon (optional)',
+  }),
+  symonUrl: Flags.string({
+    dependsOn: ['symonKey'],
+    description: 'URL of Symon',
+    hidden: false,
+  }),
+  text: Flags.string({
+    description: 'Run Monika using a Simple text file',
+    exclusive: ['postman', 'insomnia', 'sitemap', 'har'],
+  }),
+  verbose: Flags.boolean({
+    default: monikaFlagsDefaultValue.verbose,
+    description: 'Show verbose log messages',
+  }),
+  version: Flags.version({ char: 'v' }),
+}
 
-export const retryMaxDelayMs = Flags.integer({
-  default: monikaFlagsDefaultValue.retryMaxDelayMs,
-  description: `Maximum backoff retry delay, in milliseconds. Defaults to ${monikaFlagsDefaultValue.retryMaxDelayMs}ms.`,
-})
+export function sanitizeFlags(flags: Partial<MonikaFlags>): MonikaFlags {
+  return { ...monikaFlagsDefaultValue, ...flags }
+}
diff --git a/src/jobs/summary-notification.ts b/src/jobs/summary-notification.ts
index 757fc43d..830648bd 100644
--- a/src/jobs/summary-notification.ts
+++ b/src/jobs/summary-notification.ts
@@ -21,19 +21,14 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  *
  * SOFTWARE.                                                                      *
  **********************************************************************************/
-import { hostname } from 'os'
+
+import fs from 'node:fs'
+import { hostname } from 'node:os'
+import { sendNotifications } from '@hyperjumptech/monika-notification'
 import format from 'date-fns/format'
+
 import { getConfig } from '../components/config'
 import { getSummary } from '../components/logger/history'
-import { sendNotifications } from '@hyperjumptech/monika-notification'
-import {
-  getOSName,
-  getMonikaInstance,
-} from '../components/notification/alert-message'
-import { getContext } from '../context'
-import getIp from '../utils/ip'
-import { log } from '../utils/pino'
-import { publicIpAddress } from '../utils/public-ip'
 import {
   maxResponseTime,
   minResponseTime,
@@ -42,16 +37,19 @@ import {
   resetlogs,
   getLogLifeTimeInHour,
 } from '../components/logger/response-time-log'
-import fs from 'fs'
-import type { Config as IConfig } from '@oclif/core'
+import {
+  getOSName,
+  getMonikaInstance,
+} from '../components/notification/alert-message'
+import { getContext } from '../context'
 import events from '../events'
-import { Config } from '../interfaces/config'
-
+import type { Config } from '../interfaces/config'
 import { getEventEmitter } from '../utils/events'
-import type { Notification } from '@hyperjumptech/monika-notification'
-import type { Probe } from '../interfaces/probe'
 import { getErrorMessage } from '../utils/catch-error-handler'
-import { readFile } from '../utils/read-file'
+import getIp from '../utils/ip'
+import { log } from '../utils/pino'
+import { publicIpAddress } from '../utils/public-ip'
+
 const eventEmitter = getEventEmitter()
 
 type TweetMessage = {
@@ -63,7 +61,7 @@ type TweetMessage = {
 
 export async function getSummaryAndSendNotif(): Promise<void> {
   const config = getConfig()
-  const { notifications } = config
+  const { notifications, probes } = config
 
   if (!notifications) return
 
@@ -75,13 +73,10 @@ export async function getSummaryAndSendNotif(): Promise<void> {
       getOSName(),
       getMonikaInstance(privateIpAddress),
     ])
-    const {
-      numberOfIncidents,
-      numberOfProbes,
-      numberOfRecoveries,
-      numberOfSentNotifications,
-    } = summary
+    const { numberOfIncidents, numberOfRecoveries, numberOfSentNotifications } =
+      summary
     const responseTimelogLifeTimeInHour = getLogLifeTimeInHour()
+    const numberOfProbes = probes.length
     const tweetMessage = createTweetMessage({
       averageResponseTime,
       numberOfIncidents,
@@ -118,6 +113,7 @@ ${tweetMessage}
         averageResponseTime,
         responseTimelogLifeTimeInHour,
         version: userAgent,
+        numberOfProbes,
         ...summary,
       },
     }).catch((error) => log.error(`Summary notification: ${error.message}`))
@@ -130,30 +126,6 @@ ${tweetMessage}
   }
 }
 
-interface PidObject {
-  monikaPid: number
-  monikaConfigFile: string
-  monikaStartTime: Date
-  monikaProbes: Probe
-  monikaNotifs: Notification
-}
-
-/**
- * readsPidFile reads a local monika.pid file and returns the information in it
- * @returns {object} PidObject is returned
- */
-async function readPidFile(): Promise<PidObject> {
-  const fileContent = await readFile('monika.pid', 'utf8')
-  const json = JSON.parse(fileContent)
-  return {
-    monikaPid: json.monikaPid,
-    monikaConfigFile: json.monikaConfigFile,
-    monikaStartTime: json.monikaStartTime,
-    monikaProbes: json.monikaProbes,
-    monikaNotifs: json.monikaNotifs,
-  }
-}
-
 /**
  * savePidFile saves a monika.pid file with some useful information
  * @param {string} configFile is the configuration file used
@@ -185,61 +157,6 @@ eventEmitter.on(events.application.terminated, async () => {
   })
 })
 
-/**
- * getDaysHours breaks down the date into days  hours minute string
- * @param {Date} startTime is the start time
- * @returns {string} text of the date to print
- */
-function getDaysHours(startTime: Date): string {
-  let duration = Math.abs((Date.now() - new Date(startTime).getTime()) / 1000)
-  const numDays = Math.floor(duration / 86_400)
-  duration -= numDays * 86_400 // get the remaining hours
-
-  const numHours = Math.floor(duration / 3600) % 24
-  duration -= numHours * 3600 // get the remaining minutes
-
-  const numMinutes = Math.floor(duration / 60) % 60
-
-  const numSeconds = Math.floor(duration - numMinutes * 60)
-
-  return `${numDays} days, ${numHours} hours, ${numMinutes} minutes, ${numSeconds} seconds`
-}
-
-/**
- * printSummary gathers and print some stats
- * @param {object} cliConfig is oclif config structure
- * @returns Promise<void>
- */
-export async function printSummary(cliConfig: IConfig): Promise<void> {
-  try {
-    const pidObject = await readPidFile()
-    const summary = await getSummary()
-
-    const uptime = getDaysHours(pidObject.monikaStartTime)
-
-    const host = `${hostname()} (${[publicIpAddress, getIp()]
-      .filter(Boolean)
-      .join('/')})`
-
-    log.info(`Monika Summary \n
-    Monika process id \t\t: ${pidObject.monikaPid}
-    Active config file \t\t: ${pidObject.monikaConfigFile}
-    Probes set \t\t\t: ${pidObject.monikaProbes}
-    Notifications set \t\t: ${pidObject.monikaNotifs}
-    Number of incidents \t: ${summary.numberOfIncidents} in last 24hr
-    Number of recoveries \t: ${summary.numberOfRecoveries} in last 24hr
-    Number of notifications \t: ${summary.numberOfSentNotifications} in last 24h
-
-    Up time \t: ${uptime}
-    Running on \t: ${host}
-    App version : ${cliConfig.userAgent}
-
-    `)
-  } catch (error: unknown) {
-    log.error(`Summary notification: ${getErrorMessage(error)}`)
-  }
-}
-
 function createTweetMessage({
   averageResponseTime,
   numberOfIncidents,
diff --git a/src/loaders/index.ts b/src/loaders/index.ts
index c915c85e..f0c7c0d7 100644
--- a/src/loaders/index.ts
+++ b/src/loaders/index.ts
@@ -24,7 +24,8 @@
 
 import type { Config as IConfig } from '@oclif/core'
 import { isSymonModeFrom, setupConfig } from '../components/config'
-import { getContext, setContext } from '../context'
+import { openLogfile } from '../components/logger/history'
+import { getContext } from '../context'
 import events from '../events'
 import type { MonikaFlags } from '../flag'
 import { tlsChecker } from '../jobs/tls-check'
@@ -35,10 +36,8 @@ import {
   startPrometheusMetricsServer,
 } from '../plugins/metrics/prometheus'
 import { getEventEmitter } from '../utils/events'
-import { fetchAndCacheNetworkInfo } from '../utils/public-ip'
 import { jobsLoader } from './jobs'
 import { enableAutoUpdate } from '../plugins/updater'
-import { log } from '../utils/pino'
 
 // import the subscriber file to activate the event emitter subscribers
 import '../events/subscribers/application'
@@ -48,13 +47,9 @@ export default async function init(
   flags: MonikaFlags,
   cliConfig: IConfig
 ): Promise<void> {
-  const isSymonMode = isSymonModeFrom(flags)
-
-  setContext({ userAgent: cliConfig.userAgent })
-  await logRunningInfo({ isSymonMode, isVerbose: flags.verbose })
-
+  await openLogfile()
   // check if connected to STUN Server and getting the public IP in the same time
-  loopCheckSTUNServer(flags.stun)
+  await loopCheckSTUNServer(flags.stun)
   // run auto-updater
   if (flags['auto-update']) {
     await enableAutoUpdate(cliConfig, flags['auto-update'])
@@ -65,7 +60,7 @@ export default async function init(
     initPrometheus(flags.prometheus)
   }
 
-  if (!isSymonMode) {
+  if (!isSymonModeFrom(flags)) {
     await setupConfig(flags)
 
     // check TLS when Monika starts
@@ -78,26 +73,6 @@ export default async function init(
   }
 }
 
-type RunningInfoParams = { isSymonMode: boolean; isVerbose: boolean }
-
-async function logRunningInfo({ isVerbose, isSymonMode }: RunningInfoParams) {
-  if (!isVerbose && !isSymonMode) {
-    log.info('Monika is running.')
-    return
-  }
-
-  try {
-    const { city, hostname, isp, privateIp, publicIp } =
-      await fetchAndCacheNetworkInfo()
-
-    log.info(
-      `Monika is running from: ${city} - ${isp} (${publicIp}) - ${hostname} (${privateIp})`
-    )
-  } catch (error) {
-    log.warn(`Failed to obtain location/ISP info. Got: ${error}`)
-  }
-}
-
 function initPrometheus(prometheusPort: number) {
   const eventEmitter = getEventEmitter()
   const {
diff --git a/src/symon/index.ts b/src/symon/index.ts
index 3b67893f..5083f423 100644
--- a/src/symon/index.ts
+++ b/src/symon/index.ts
@@ -174,12 +174,12 @@ export default class SymonClient {
   private url: string
 
   constructor({
-    'symon-api-version': apiVersion = SYMON_API_VERSION.v1,
+    'symon-api-version': apiVersion,
     symonKey = '',
     symonLocationId = '',
     symonMonikaId = '',
-    symonReportInterval = 10_000,
-    symonReportLimit = 100,
+    symonReportInterval,
+    symonReportLimit,
     symonUrl = '',
   }: SymonClientParams) {
     this.apiKey = symonKey


Test patch:
diff --git a/src/components/logger/flush.test.ts b/src/components/logger/flush.test.ts
index b9db1864..60fe3523 100644
--- a/src/components/logger/flush.test.ts
+++ b/src/components/logger/flush.test.ts
@@ -22,14 +22,15 @@
  * SOFTWARE.                                                                      *
  **********************************************************************************/
 
-import sinon from 'sinon'
+import { type SinonStub, assert, stub } from 'sinon'
+import { getContext, resetContext, setContext } from '../../context'
 import * as history from './history'
 import { flush } from './flush'
 
-let flushAllLogsStub: sinon.SinonStub
+let flushAllLogsStub: SinonStub
 
 beforeEach(() => {
-  flushAllLogsStub = sinon.stub(history, 'flushAllLogs').resolves()
+  flushAllLogsStub = stub(history, 'flushAllLogs').resolves()
 })
 
 afterEach(() => {
@@ -39,11 +40,16 @@ afterEach(() => {
 describe('Flush command', () => {
   describe('Force', () => {
     it('should flush records without asking for confirmation', async () => {
+      // arrange
+      setContext({ flags: { ...getContext().flags, force: true } })
+
       // act
-      await flush(true)
+      await flush()
 
       // assert
-      sinon.assert.calledOnce(flushAllLogsStub)
+      assert.calledOnce(flushAllLogsStub)
+
+      resetContext()
     })
   })
 })
diff --git a/src/components/logger/startup-message.test.ts b/src/components/logger/startup-message.test.ts
index 0b3f49b9..c27f3209 100644
--- a/src/components/logger/startup-message.test.ts
+++ b/src/components/logger/startup-message.test.ts
@@ -24,6 +24,7 @@
 
 import chai, { expect } from 'chai'
 import spies from 'chai-spies'
+import { sanitizeFlags } from '../../flag'
 import type { Config } from '../../interfaces/config'
 import { log } from '../../utils/pino'
 import { logStartupMessage } from './startup-message'
@@ -82,12 +83,12 @@ describe('Startup message', () => {
       // act
       logStartupMessage({
         config: { probes: [] },
-        flags: {
+        flags: sanitizeFlags({
           config: [],
           symonKey: 'secret-key',
           symonUrl: 'https://example.com',
           verbose: false,
-        },
+        }),
         isFirstRun: false,
       })
 
@@ -102,7 +103,12 @@ describe('Startup message', () => {
         // arrange
         logStartupMessage({
           config: { probes: [] },
-          flags: { config: [], symonKey: '', symonUrl: '', verbose: false },
+          flags: sanitizeFlags({
+            config: [],
+            symonKey: '',
+            symonUrl: '',
+            verbose: false,
+          }),
           isFirstRun: false,
         })
 
@@ -116,7 +122,12 @@ describe('Startup message', () => {
         // arrange
         logStartupMessage({
           config: defaultConfig,
-          flags: { config: [], symonKey: '', symonUrl: '', verbose: false },
+          flags: sanitizeFlags({
+            config: [],
+            symonKey: '',
+            symonUrl: '',
+            verbose: false,
+          }),
           isFirstRun: true,
         })
 
@@ -128,7 +139,12 @@ describe('Startup message', () => {
         // arrange
         logStartupMessage({
           config: defaultConfig,
-          flags: { config: [], symonKey: '', symonUrl: '', verbose: false },
+          flags: sanitizeFlags({
+            config: [],
+            symonKey: '',
+            symonUrl: '',
+            verbose: false,
+          }),
           isFirstRun: false,
         })
 
@@ -140,7 +156,12 @@ describe('Startup message', () => {
         // arrange
         logStartupMessage({
           config: defaultConfig,
-          flags: { config: [], symonKey: '', symonUrl: '', verbose: false },
+          flags: sanitizeFlags({
+            config: [],
+            symonKey: '',
+            symonUrl: '',
+            verbose: false,
+          }),
           isFirstRun: true,
         })
 
@@ -156,7 +177,12 @@ describe('Startup message', () => {
             ...defaultConfig,
             notifications: [],
           },
-          flags: { config: [], symonKey: '', symonUrl: '', verbose: false },
+          flags: sanitizeFlags({
+            config: [],
+            symonKey: '',
+            symonUrl: '',
+            verbose: false,
+          }),
           isFirstRun: true,
         })
 
@@ -180,7 +206,12 @@ describe('Startup message', () => {
               },
             ],
           },
-          flags: { config: [], symonKey: '', symonUrl: '', verbose: true },
+          flags: sanitizeFlags({
+            config: [],
+            symonKey: '',
+            symonUrl: '',
+            verbose: true,
+          }),
           isFirstRun: true,
         })
         // assert
@@ -205,7 +236,12 @@ describe('Startup message', () => {
               },
             ],
           },
-          flags: { config: [], symonKey: '', symonUrl: '', verbose: true },
+          flags: sanitizeFlags({
+            config: [],
+            symonKey: '',
+            symonUrl: '',
+            verbose: true,
+          }),
           isFirstRun: true,
         })
         // assert
@@ -221,7 +257,12 @@ describe('Startup message', () => {
         // arrange
         logStartupMessage({
           config: defaultConfig,
-          flags: { config: [], symonKey: '', symonUrl: '', verbose: true },
+          flags: sanitizeFlags({
+            config: [],
+            symonKey: '',
+            symonUrl: '',
+            verbose: true,
+          }),
           isFirstRun: true,
         })
         // assert
@@ -254,7 +295,12 @@ describe('Startup message', () => {
               },
             ],
           },
-          flags: { config: [], symonKey: '', symonUrl: '', verbose: true },
+          flags: sanitizeFlags({
+            config: [],
+            symonKey: '',
+            symonUrl: '',
+            verbose: true,
+          }),
           isFirstRun: true,
         })
         // assert
@@ -268,7 +314,12 @@ describe('Startup message', () => {
         // arrange
         logStartupMessage({
           config: defaultConfig,
-          flags: { config: [], symonKey: '', symonUrl: '', verbose: true },
+          flags: sanitizeFlags({
+            config: [],
+            symonKey: '',
+            symonUrl: '',
+            verbose: true,
+          }),
           isFirstRun: true,
         })
         // assert
@@ -295,7 +346,12 @@ describe('Startup message', () => {
               },
             ],
           },
-          flags: { config: [], symonKey: '', symonUrl: '', verbose: true },
+          flags: sanitizeFlags({
+            config: [],
+            symonKey: '',
+            symonUrl: '',
+            verbose: true,
+          }),
           isFirstRun: true,
         })
         // assert
@@ -313,7 +369,12 @@ describe('Startup message', () => {
             ...defaultConfig,
             notifications: [{ id: 'UVIsL', type: 'desktop', data: undefined }],
           },
-          flags: { config: [], symonKey: '', symonUrl: '', verbose: true },
+          flags: sanitizeFlags({
+            config: [],
+            symonKey: '',
+            symonUrl: '',
+            verbose: true,
+          }),
           isFirstRun: true,
         })
         // assert
@@ -341,7 +402,12 @@ describe('Startup message', () => {
               },
             ],
           },
-          flags: { config: [], symonKey: '', symonUrl: '', verbose: true },
+          flags: sanitizeFlags({
+            config: [],
+            symonKey: '',
+            symonUrl: '',
+            verbose: true,
+          }),
           isFirstRun: true,
         })
         // assert
@@ -368,7 +434,12 @@ describe('Startup message', () => {
               },
             ],
           },
-          flags: { config: [], symonKey: '', symonUrl: '', verbose: true },
+          flags: sanitizeFlags({
+            config: [],
+            symonKey: '',
+            symonUrl: '',
+            verbose: true,
+          }),
           isFirstRun: true,
         })
         // assert
@@ -390,7 +461,12 @@ describe('Startup message', () => {
               },
             ],
           },
-          flags: { config: [], symonKey: '', symonUrl: '', verbose: true },
+          flags: sanitizeFlags({
+            config: [],
+            symonKey: '',
+            symonUrl: '',
+            verbose: true,
+          }),
           isFirstRun: true,
         })
         // assert
@@ -404,12 +480,12 @@ describe('Startup message', () => {
       // act
       logStartupMessage({
         config: defaultConfig,
-        flags: {
+        flags: sanitizeFlags({
           config: ['https://example.com/monika.yaml'],
           symonKey: '',
           symonUrl: '',
           verbose: false,
-        },
+        }),
         isFirstRun: false,
       })
 
@@ -421,12 +497,12 @@ describe('Startup message', () => {
       // act
       logStartupMessage({
         config: defaultConfig,
-        flags: {
+        flags: sanitizeFlags({
           config: ['./monika.yaml'],
           symonKey: '',
           symonUrl: '',
           verbose: false,
-        },
+        }),
         isFirstRun: false,
       })
 
diff --git a/src/symon/index.test.ts b/src/symon/index.test.ts
index 31a31b88..7fa71d44 100644
--- a/src/symon/index.test.ts
+++ b/src/symon/index.test.ts
@@ -26,7 +26,6 @@ import { expect } from '@oclif/test'
 import { type DefaultBodyType, HttpResponse, http } from 'msw'
 import { setupServer } from 'msw/node'
 
-import { monikaFlagsDefaultValue } from '../flag'
 import type { Config } from '../interfaces/config'
 import type { Probe } from '../interfaces/probe'
 
@@ -36,6 +35,7 @@ import { deleteProbe, findProbe, getProbes } from '../components/config/probe'
 import { validateProbes } from '../components/config/validation'
 import { addIncident, findIncident } from '../components/incident'
 import events from '../events'
+import { sanitizeFlags } from '../flag'
 import { getEventEmitter } from '../utils/events'
 import { getErrorMessage } from '../utils/catch-error-handler'
 import { getProbeState, initializeProbeStates } from '../utils/probe-state'
@@ -109,13 +109,10 @@ describe('Symon initiate', () => {
   beforeEach(() => {
     resetContext()
     setContext({
-      flags: {
-        ...getContext().flags,
+      flags: sanitizeFlags({
         symonUrl: 'http://localhost:4000',
         symonKey: 'random-key',
-        symonGetProbesIntervalMs:
-          monikaFlagsDefaultValue.symonGetProbesIntervalMs,
-      },
+      }),
     })
 
     // reset probe cache
@@ -134,11 +131,10 @@ describe('Symon initiate', () => {
   it('should send handshake data on initiate', async () => {
     setContext({
       userAgent: 'v1.5.0',
-      flags: {
-        ...getContext().flags,
+      flags: sanitizeFlags({
         symonUrl: 'http://localhost:4000',
         symonKey: 'random-key',
-      },
+      }),
     })
     let body: DefaultBodyType = {}
     // mock the outgoing requests
@@ -159,10 +155,12 @@ describe('Symon initiate', () => {
       )
     )
 
-    const symon = new SymonClient({
-      symonUrl: 'http://localhost:4000',
-      symonKey: 'abcd',
-    })
+    const symon = new SymonClient(
+      sanitizeFlags({
+        symonUrl: 'http://localhost:4000',
+        symonKey: 'abcd',
+      })
+    )
     await symon.initiate()
     await symon.stop()
 
@@ -179,10 +177,12 @@ describe('Symon initiate', () => {
   }).timeout(15_000)
 
   it('should fetch probes config on initiate', async () => {
-    const symon = new SymonClient({
-      symonUrl: 'http://localhost:4000',
-      symonKey: 'abcd',
-    })
+    const symon = new SymonClient(
+      sanitizeFlags({
+        symonUrl: 'http://localhost:4000',
+        symonKey: 'abcd',
+      })
+    )
 
     expect(getContext().config).to.be.undefined
 
@@ -213,10 +213,12 @@ describe('Symon initiate', () => {
       })
     )
 
-    const symon = new SymonClient({
-      symonUrl: 'http://localhost:4000',
-      symonKey: 'abcd',
-    })
+    const symon = new SymonClient(
+      sanitizeFlags({
+        symonUrl: 'http://localhost:4000',
+        symonKey: 'abcd',
+      })
+    )
     let errorMessage = ''
 
     try {
@@ -248,11 +250,13 @@ describe('Symon initiate', () => {
         }
       )
     )
-    const symon = new SymonClient({
-      symonUrl: 'http://localhost:4000',
-      symonKey: 'abcd',
-      symonMonikaId: '1234',
-    })
+    const symon = new SymonClient(
+      sanitizeFlags({
+        symonUrl: 'http://localhost:4000',
+        symonKey: 'abcd',
+        symonMonikaId: '1234',
+      })
+    )
     await symon.initiate()
 
     // act
@@ -306,17 +310,18 @@ describe('Symon initiate', () => {
     )
     const symonGetProbesIntervalMs = 100
     setContext({
-      flags: {
-        ...getContext().flags,
+      flags: sanitizeFlags({
         symonUrl: 'http://localhost:4000',
         symonKey: 'random-key',
         symonGetProbesIntervalMs,
-      },
-    })
-    const symon = new SymonClient({
-      symonUrl: 'http://localhost:4000',
-      symonKey: 'abcd',
+      }),
     })
+    const symon = new SymonClient(
+      sanitizeFlags({
+        symonUrl: 'http://localhost:4000',
+        symonKey: 'abcd',
+      })
+    )
 
     // 1. Check initial probe cache
     expect(getProbes()).deep.eq([])
@@ -359,17 +364,18 @@ describe('Symon initiate', () => {
     )
     const symonGetProbesIntervalMs = 100
     setContext({
-      flags: {
-        ...getContext().flags,
+      flags: sanitizeFlags({
         symonUrl: 'http://localhost:4000',
         symonKey: 'random-key',
         symonGetProbesIntervalMs,
-      },
-    })
-    const symon = new SymonClient({
-      symonUrl: 'http://localhost:4000',
-      symonKey: 'abcd',
+      }),
     })
+    const symon = new SymonClient(
+      sanitizeFlags({
+        symonUrl: 'http://localhost:4000',
+        symonKey: 'abcd',
+      })
+    )
 
     // 1. Check initial probe cache
     expect(getProbes()).deep.eq([])
@@ -413,17 +419,18 @@ describe('Symon initiate', () => {
     )
     const symonGetProbesIntervalMs = 100
     setContext({
-      flags: {
-        ...getContext().flags,
+      flags: sanitizeFlags({
         symonUrl: 'http://localhost:4000',
         symonKey: 'random-key',
         symonGetProbesIntervalMs,
-      },
-    })
-    const symon = new SymonClient({
-      symonUrl: 'http://localhost:4000',
-      symonKey: 'abcd',
+      }),
     })
+    const symon = new SymonClient(
+      sanitizeFlags({
+        symonUrl: 'http://localhost:4000',
+        symonKey: 'abcd',
+      })
+    )
 
     // 1. Check initial probe cache
     expect(getProbes()).deep.eq([])
@@ -467,17 +474,18 @@ describe('Symon initiate', () => {
 
     const symonGetProbesIntervalMs = 100
     setContext({
-      flags: {
-        ...getContext().flags,
+      flags: sanitizeFlags({
         symonUrl: 'http://localhost:4000',
         symonKey: 'random-key',
         symonGetProbesIntervalMs,
-      },
-    })
-    const symon = new SymonClient({
-      symonUrl: 'http://localhost:4000',
-      symonKey: 'abcd',
+      }),
     })
+    const symon = new SymonClient(
+      sanitizeFlags({
+        symonUrl: 'http://localhost:4000',
+        symonKey: 'abcd',
+      })
+    )
 
     // 1. Check initial probe cache
     expect(getProbes()).deep.eq([])