MarcSchaetz / vuejs3-logger

Provides customizable logging functionality for Vue.js. Compatible with Vue3.
MIT License
9 stars 4 forks source link

How to use out of setup()? #1

Closed sheoak closed 3 years ago

sheoak commented 3 years ago

The documention precise how to use vuejs3-logger outside of components, but I get an error when trying to use it outside (vuex store):

Vue warn]: inject() can only be used inside setup() or functional components.
import { inject } from 'vue'
…
async my_vuex_method() {
    …
    const logger = inject('vuejs3-logger')
    …
}

Thanks for porting vuejs-logger to vue3!

MarcSchaetz commented 3 years ago

@sheoak Hi, thanks for filing an issue. I will try to look at it today and give you feedback.

chrisspiegl commented 3 years ago

@MarcSchaetz funny you just replied here a few minutes ago. I actually built a little solution for myself here and would like to suggest it as a implementation.

Basically, the behavior of other plugins nowadays seems to be to provide a createLogger function as a named export.

This createLogger then builds the logger and also has a key install which would be used by the app.use to call with the app and possible options.

All this could then be used by the user in a plugins/logger.js to build their own logger which has the same preferences inside components and outside.

In the mean time, I basically did the same for myself with a bit of a workaround (wrapping the vuejs3-logger in it's own plugin createLogger 🙈.

Maybe the code below will be helpful (it's workaround):

import VueLogger from 'vuejs3-logger'

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

export const options = {
  isEnabled: true,
  logLevel: isProduction ? 'error' : 'debug',
  stringifyArguments: false,
  showLogLevel: true,
  showMethodName: true,
  separator: ':',
  showConsoleColors: true,
}

function createLogger(options) {
  return {
    install (app, installOptions = {}) {
      options = {...options, ...installOptions }
      app.use(VueLogger, options)
      console.log(`INSTALLING THE LOGGER`)
      Object.assign(this, app.$log)
    }
  }
}

const loggerCreated = createLogger(options)

// outputs only the `{ install: … }` because at this point the logger is not installed and thus the `Object.assign` was not run. This however is also not possible to do any different way without going into the guts of the `vuejs3-logger` because the necessary function (`initLoggerInstance `) is currently `private`.
// console.log(`loggerCreated:`, loggerCreated) 

export const logger = loggerCreated
export const log = loggerCreated

The improvement with official support of a createLogger function would solve all the issues and would make the logger available upon first call and not just after it actually was initiated with app.use.

Again, hope this helps and am looking forward to an update to this plugin 🌸.

Oh… and P.S.: hi from Germany 👍 just a few hundred km north from you 🚗 .

chrisspiegl commented 3 years ago

Quick followup: with a bit more testing, this workaround does not work properly. I am experimenting with a createLogger function in my own local copy of vue3-logger however, I am not confident in Typescript and all that 🙈 . But maybe I can provide a scaffold based on my findings.

chrisspiegl commented 3 years ago

Further findings: This time it actually worked 🙈. The logger is now accessible outside of components and can be loaded by simply doing import { log } from '@/plugins/logger' in my project.

Please note again, I am not proficient in Typescript so this is probably "ugly" for some 🙈.

Here is the function I added to the vue3-logger.ts file:

public createLogger(options: ILoggerOptions) {
    options = Object.assign(this.getDefaultOptions(), options);

    if (this.isValidOptions(options, this.logLevels)) {
      const $log = this.initLoggerInstance(options, this.logLevels);
      $log.install = function (Vue: any, options: ILoggerOptions) {
        Vue.provide('vuejs3-logger', $log);
        Vue.config.globalProperties.$log = $log;
      }
      return $log
    } else {
        throw new Error(this.errorMessage);
    }
}

And below is my plugins/logger.js file which loads and creates the logger.

I have not found a way to directly export the createLogger so that I can simply do a import { createLogger } from 'vuejs3-logger' for now. But maybe a official release will fix that and someone with more understanding can get that done 🥇 .

import VueLogger from 'vuejs3-logger/src'

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

export const options = {
  isEnabled: true,
  logLevel: isProduction ? 'error' : 'debug',
  stringifyArguments: false,
  showLogLevel: true,
  showMethodName: true,
  separator: ':',
  showConsoleColors: true,
}

const loggerCreated = VueLogger.createLogger(options)

export const logger = loggerCreated
export const log = loggerCreated
MarcSchaetz commented 3 years ago

The documention precise how to use vuejs3-logger outside of components, but I get an error when trying to use it outside (vuex store):

Vue warn]: inject() can only be used inside setup() or functional components.
import { inject } from 'vue'
…
async my_vuex_method() {
    …
    const logger = inject('vuejs3-logger')
    …
}

Thanks for porting vuejs-logger to vue3!

@sheoak Ok, so I looked into it. The setup() is part of the dependency injection system of Vue3 and can only be called by components.

There are two solutions which I think are the simplest to go for.

Using the build-in Vuex logger

Vuex comes with it's own logger implementation, which can print useful informations about statechanges out of the box.

import { createLogger } from 'vuex'

const store = new Vuex.Store({
  plugins: [createLogger()]
})
Output:
mutation trylog @ 18:46:41.820
  prev state {}
  mutation {type: "trylog", payload: undefined}payload: undefinedtype: "trylog"
  next state {}

Call app.provide() to store the logger outside components.

createApp(App) returns you a reference to the application. There you can call provide() to register a value to a key. The VueLogger stores itself into app.config.globalProperties.$log. So you could do something like this:

// main.ts

import VueLogger from 'vuejs3-logger';

const app = createApp(App)        // Create the application
                .use(VueLogger);  // Register VueLogger plugin

app.provide('vuejs3-logger-vuex', app.config.globalProperties.$log);  // Register the logger instance with another key
app.use(store);  // Register Vuex Plugin

app.mount('#app')
// store.ts
// ...

const logger = inject('vuejs3-logger-vuex');
logger.debug('Log message from store');

// ...
MarcSchaetz commented 3 years ago

Further findings: This time it actually worked see_no_evil. The logger is now accessible outside of components and can be loaded by simply doing import { log } from '@/plugins/logger' in my project.

Please note again, I am not proficient in Typescript so this is probably "ugly" for some see_no_evil.

Here is the function I added to the vue3-logger.ts file:

public createLogger(options: ILoggerOptions) {
    options = Object.assign(this.getDefaultOptions(), options);

    if (this.isValidOptions(options, this.logLevels)) {
      const $log = this.initLoggerInstance(options, this.logLevels);
      $log.install = function (Vue: any, options: ILoggerOptions) {
        Vue.provide('vuejs3-logger', $log);
        Vue.config.globalProperties.$log = $log;
      }
      return $log
    } else {
        throw new Error(this.errorMessage);
    }
}

And below is my plugins/logger.js file which loads and creates the logger.

I have not found a way to directly export the createLogger so that I can simply do a import { createLogger } from 'vuejs3-logger' for now. But maybe a official release will fix that and someone with more understanding can get that done 1st_place_medal .

import VueLogger from 'vuejs3-logger/src'

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

export const options = {
  isEnabled: true,
  logLevel: isProduction ? 'error' : 'debug',
  stringifyArguments: false,
  showLogLevel: true,
  showMethodName: true,
  separator: ':',
  showConsoleColors: true,
}

const loggerCreated = VueLogger.createLogger(options)

export const logger = loggerCreated
export const log = loggerCreated

@chrisspiegl Thanks for the suggestion. I created a new issue (#2) for that. Would you please post your last comment there again? Thank you very much.

chrisspiegl commented 3 years ago

@MarcSchaetz Thank you for the suggestions with the vuex logger that is incredibly helpful and I did not know about before.

And I have posted my comment in the other issue 👍.

sheoak commented 3 years ago

Great! I will try this as soon as possible, very helpful thank you!

whaliendev commented 3 years ago

The documention precise how to use vuejs3-logger outside of components, but I get an error when trying to use it outside (vuex store):

Vue warn]: inject() can only be used inside setup() or functional components.
import { inject } from 'vue'
…
async my_vuex_method() {
    …
    const logger = inject('vuejs3-logger')
    …
}

Thanks for porting vuejs-logger to vue3!

@sheoak Ok, so I looked into it. The setup() is part of the dependency injection system of Vue3 and can only be called by components.

There are two solutions which I think are the simplest to go for.

Using the build-in Vuex logger

Vuex comes with it's own logger implementation, which can print useful informations about statechanges out of the box.

import { createLogger } from 'vuex'

const store = new Vuex.Store({
  plugins: [createLogger()]
})
Output:
mutation trylog @ 18:46:41.820
  prev state {}
  mutation {type: "trylog", payload: undefined}payload: undefinedtype: "trylog"
  next state {}

Call app.provide() to store the logger outside components.

createApp(App) returns you a reference to the application. There you can call provide() to register a value to a key. The VueLogger stores itself into app.config.globalProperties.$log. So you could do something like this:

// main.ts

import VueLogger from 'vuejs3-logger';

const app = createApp(App)        // Create the application
                .use(VueLogger);  // Register VueLogger plugin

app.provide('vuejs3-logger-vuex', app.config.globalProperties.$log);  // Register the logger instance with another key
app.use(store);  // Register Vuex Plugin

app.mount('#app')
// store.ts
// ...

const logger = inject('vuejs3-logger-vuex');
logger.debug('Log message from store');

// ...

How can I use it in an external js file? for example, a js file encapsulates axios request? According to the api refrence, the provide/inject feature cann't be called outside setup function. Except exporting the app instance createApp function returned, is there a more elegant way to do this?

laygir commented 1 year ago

I've been having similar issues, I wanted to use vuejs3-logger in Pinia but provide/inject didn't work somehow.. This is how I ended up doing, not sure if this is too hacky..

const app = createApp(App);
const pinia = createPinia();

pinia.use(({ store }) => {
  store.$log = markRaw(app.config.globalProperties.$log);
});

app.use(pinia);
app.use(router);
app.use(logger);

app.mount('#app');