How to have logging in Vue.js without console.log?
Asked Answered
R

3

7

I want to have console logging while I'm developing in Vue.js but want it disabled for production.

There is a module vuejs-logger that works fine inside of a Vue component and will be disabled in production mode. But it will not work (for instance) in my service files or library files (outside of the Vue instance) nor in Vuex.

Does anyone have any other suggestions? Vue.js integrated or otherwise?

Stay safe, TIA

Edit: Just to clarify, vuejs-logger's Typescript integration is the real problem. It throws up so many TS errors that I cannot use it. The actual JS logging seems to work... I'm still interested in other solutions that may work smoothly.

Retinoscopy answered 11/3, 2020 at 10:56 Comment(0)
T
14

Consider building your own simple wrapper which will work everywhere, assuming you are using Webpack, like the following:

declare const NODE_ENV: any;

export function log(message: String, level?: 'info' | 'warn' | 'error') {

    // WHEN RUNNING WEBPACK WITH `PRODUCTION` build,
    // IT WILL REMOVE THE FOLLOWING CODE.

    if (NODE_ENV !== 'production') {

        if (level === 'error') {
            console.error(message);
        } else if (level === 'warn') {
            console.warn(message);
        } else {
            console.log(message);
        }
    }
}

Being a simple function, you can literally import this anywhere in your code including component files or other non-component files. The important point here is to note the use of the NODE_ENV variable which will be injected by Webpack when running in Production mode. Similar setups exist for Rollup and other bundlers. Read more about this here. When Webpack is bundling the code with production mode, it will ignore the code inside if (NODE_ENV !== 'production') { /* some code */ } construct.

Also, if you need to wrap it inside a plugin so that it is available on every component instance using the this pointer then:

const MyLogger = {
    install(Vue: Vue, options: any) {

        // Add an instance method
        Vue.prototype.log = log;
    }
};

Vue.install(MyLogger);

Of course, if you are using TypeScript, then you must teach TypeScript using module augmentation like this:

import Vue from 'vue';

declare module 'vue/types/vue' {
    interface Vue {
        log(message: string, level?: 'info' | 'warn' | 'error'): void;
    }
}

The above snippet should be present inside your typings folder like specified using tsconfig.json. The directory path would be: ROOT/typings/vue/index.d.ts.

In tsconfig.json file, typings array should be set to:

"typeRoots": [
    "./node_modules/@types",
    "./typings"
],
Trilley answered 11/3, 2020 at 12:2 Comment(1)
Thanks for the very thorough answer. I'll give both ideas a try later today. Great!Retinoscopy
G
4

It is also possible to use the vuejs3-logger in modules that are not components. These are the required steps:

  1. create a module logger.js with content
import VueLogger from 'vuejs3-logger'
import { createApp } from 'vue'

const isProduction = process.env.NODE_ENV === 'production';

const vlOptions = {
    isEnabled: true,
    logLevel : isProduction ? 'info' : 'debug',
    stringifyArguments : false,
    showLogLevel : true,
    showMethodName : true,
    separator: '|',
    showConsoleColors: true
};

// create a dummy app that can be used as logger in other js modules
const app = createApp({})
app.use(VueLogger, vlOptions);
const logger = app.config.globalProperties.$log;

export {VueLogger, vlOptions, logger};
  1. use this to create your Vue app as usual
import {createApp} from 'vue'
import {VueLogger, vlOptions} from './logger.js'
import App from '../components/App.vue'

createApp(App)
  .use(VueLogger, vlOptions)
  .mount('#app');
  1. In your external JavaScript module, do this:
import {logger} from './logger.js'

logger.debug("Hi there!")

The point is that when you change the logging level in logger.js, all your JavaScript modules will adapt to this logging level as well.

Grandee answered 15/11, 2021 at 19:19 Comment(2)
This logger is not accessible outside of the component. like in store files. I am using typescript. pania store.Galasyn
As mentioned in the answer this solution works in JavaScript modules outside components, e.g. it works in pinia store files.Grandee
T
0

A simple Vue 3 & Pinia & Vite & Typescript solution based on answers in this thread. Thought I might just share for reference.

log.ts

import type { App } from 'vue'
import 'pinia'

const logLevelDebug = 'debug'
const logLevelInfo = 'info'
const logLevelWarn = 'warn'
const logLevelError = 'error'

export const logger = {
  install(app: App) {
    app.config.globalProperties.$logDebug = logDebug
    app.config.globalProperties.$logInfo = logInfo
    app.config.globalProperties.$logWarn = logWarn
    app.config.globalProperties.$logError = logError
  }
}

export const piniaLogger = function () {
  return {
    $logDebug: logDebug,
    $logInfo: logInfo,
    $logWarn: logWarn,
    $logError: logError
  }
}

// Use this.$logX() in vue components
declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    $logDebug: (message: string, obj?: any) => void
    $logInfo: (message: string, obj?: any) => void
    $logWarn: (message: string, obj?: any) => void
    $logError: (message: string, obj?: any) => void
  }
}

// Use this.$logX() in pinia stores
declare module 'pinia' {
  export interface PiniaCustomProperties {
    $logDebug: (message: string, obj?: any) => void
    $logInfo: (message: string, obj?: any) => void
    $logWarn: (message: string, obj?: any) => void
    $logError: (message: string, obj?: any) => void
  }
}

function logDebug(message: string, obj?: any) {
  if (
    import.meta.env.MODE !== 'production' &&
    [logLevelDebug, logLevelInfo, logLevelWarn, logLevelError].includes(import.meta.env.VITE_LOG_LEVEL)
  ) {
    console.debug(message)
    if (obj) {
      console.debug(obj)
    }
  }
}

function logInfo(message: string, obj?: any) {
  if (
    import.meta.env.MODE !== 'production' &&
    [logLevelInfo, logLevelWarn, logLevelError].includes(import.meta.env.VITE_LOG_LEVEL)
  ) {
    console.info(message)
    if (obj) {
      console.info(obj)
    }
  }
}

function logWarn(message: string, obj?: any) {
  if (
    import.meta.env.MODE !== 'production' &&
    [logLevelWarn, logLevelError].includes(import.meta.env.VITE_LOG_LEVEL)
  ) {
    console.warn(message)
    if (obj) {
      console.warn(obj)
    }
  }
}

function logError(message: string, obj?: any) {
  if (
    import.meta.env.MODE !== 'production' &&
    [logLevelError].includes(import.meta.env.VITE_LOG_LEVEL)
  ) {
    console.error(message)
    if (obj) {
      console.error(obj)
    }
  }
}

It makes sure that you can use this.$logX() in components and stores.

.env

VITE_LOG_LEVEL=error # (debug, info, warn, error)

VITE_ prefix is important, or else Vite won't pick up your variable.

And in main.ts

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from '@/App.vue'
import { logger, piniaLogger } from '@/log'

createApp(App).use(createPinia(piniaLogger)).use(logger).mount('#app')
Tantalizing answered 21/4 at 13:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.