quale dovrebbe essere la posizione del logger nell'elenco dei parametri [chiuso]


12

Nel mio codice inserisco un logger in molte delle mie classi attraverso l'elenco dei parametri del loro costruttore

Ho notato che l'ho messo a caso: a volte è il primo della lista, a volte l'ultimo, a volte in mezzo

Hai qualche preferenza? La mia intuizione dice che la coerenza è utile in questo caso e la mia preferenza personale è quella di metterla al primo posto in modo che sia più facile essere notato quando manca e più facile saltare quando è lì.

Risposte:


31

I logger sono ciò che chiamiamo "preoccupazione trasversale". Si arrendono a tecniche come la programmazione orientata agli aspetti; se hai un modo per decorare le tue classi con un attributo o eseguire un po 'di intreccio di codice, questo è un buon modo per ottenere capacità di registrazione mantenendo "puri" i tuoi oggetti e gli elenchi di parametri.

L'unico motivo per cui potresti voler passare in un logger è se vuoi specificare diverse implementazioni di logging, ma la maggior parte dei framework di logging ha la flessibilità di permetterti di configurarli, diciamo, per differenti target di logging. (file di registro, Gestione eventi di Windows, ecc.)

Per questi motivi, preferisco rendere la registrazione una parte naturale del sistema, piuttosto che passare un logger in ogni classe per scopi di registrazione. Quindi, ciò che faccio generalmente è fare riferimento allo spazio dei nomi di registrazione appropriato e utilizzare semplicemente il logger nelle mie classi.

Se vuoi ancora passare un logger, la mia preferenza è che tu lo renda l' ultimo parametro nell'elenco dei parametri (rendilo un parametro opzionale, se possibile). Avere il primo parametro non ha molto senso; il primo parametro dovrebbe essere il più importante, quello più rilevante per il funzionamento della classe.


11
+! mantenere il logger fuori dai parametri del costruttore; preferisco un registratore statico, quindi so che tutti usano la stessa cosa
Steven A. Lowe,

1
@ StevenA.Lowe Si noti tuttavia che l'utilizzo di logger statici può causare altri problemi lungo la linea se non si presta attenzione (ad es. Ordine di inizializzazione statica fiasco in C ++). Concordo sul fatto che avere il logger come entità accessibile a livello globale abbia i suoi vantaggi, ma dovrei valutare attentamente se e come un tale progetto si adatta all'architettura complessiva.
ComicSansMS

@ComicSansMS: ovviamente, oltre a problemi di threading ecc. Solo una preferenza personale - "il più semplice possibile, ma non più semplice";)
Steven A. Lowe

Avere un logger statico può essere un problema. Rende più difficile l'iniezione di dipendenza (a meno che tu non intenda un logger singleton istanziato dal tuo contenitore DI), e se mai vuoi cambiare la tua architettura potrebbe essere doloroso. In questo momento, ad esempio, sto usando le funzioni di Azure che passano in un logger come parametro per ogni esecuzione della funzione, quindi mi dispiace aver passato il mio logger attraverso il costruttore.
Slothario,

@Slothario: questa è la bellezza di un logger statico. Non è richiesta alcuna iniezione di alcun tipo.
Robert Harvey,

7

Nelle lingue con sovraccarico di funzioni, direi che più è probabile che un argomento sia facoltativo, più dovrebbe essere giusto. Ciò crea coerenza quando si creano sovraccarichi in cui mancano:

foo(mandatory);
foo(mandatory, optional);
foo(mandatory, optional, evenMoreOptional);

Nei linguaggi funzionali è più utile il contrario: più è probabile che tu scelga alcuni valori predefiniti, più dovrebbe rimanere. Ciò semplifica la specializzazione della funzione semplicemente applicando argomenti ad essa:

addThreeToList = map (+3)

Tuttavia, come indicato nelle altre risposte, probabilmente non si desidera passare esplicitamente il logger nell'elenco degli argomenti di ogni classe nel sistema.


3

Puoi sicuramente dedicare molto tempo a progettare eccessivamente questo problema.

Per le lingue con implementazioni di registrazione canonica, basta creare un'istanza del registratore canonico direttamente in ogni classe.

Per le lingue senza un'implementazione canonica, prova a trovare un framework di facciata di registrazione e attenersi ad esso. slf4j è una buona scelta in Java.

Personalmente preferirei attenermi a un'unica implementazione di registrazione concreta e inviare tutto a syslog. Tutti i buoni strumenti di analisi dei log sono in grado di combinare i log di sysout da più server di app in un report completo.

Quando una firma di funzione include uno o due servizi di dipendenza e alcuni argomenti "reali", inserisco le dipendenze per ultime:

int calculateFooBarSum(int foo, int bar, IntegerSummationService svc)

Poiché i miei sistemi tendono ad avere solo cinque o meno di tali servizi, mi assicuro sempre che i servizi siano inclusi nello stesso ordine in tutte le firme delle funzioni. L'ordine alfabetico è buono come un altro. (A parte: mantenere questo approccio metodologico per la gestione del mutex ridurrà anche le tue possibilità di sviluppare deadlock.)

Se ti ritrovi a iniettare più di una dozzina di dipendenze nella tua app, probabilmente il sistema deve essere suddiviso in sottosistemi separati (oserei dire microservizi?).


Mi sembra imbarazzante usare l'iniezione di proprietà per chiamare CalcFooBarSum.
un phu il

2

I logger sono un po 'un caso speciale perché devono essere disponibili letteralmente ovunque.

Se hai deciso di voler passare un logger nel costruttore di ogni classe, allora dovresti sicuramente impostare una convenzione coerente per il modo in cui lo fai (ad esempio, sempre il primo parametro, sempre passato per riferimento, l'elenco di inizializzazione del costruttore inizia sempre con m_logger (theLogger), ecc.). Qualunque cosa che verrà utilizzata nell'intera base di codice un giorno trarrà beneficio dalla coerenza.

In alternativa, è possibile che ogni classe crei un'istanza del proprio oggetto logger, senza che sia necessario alcun passaggio. Il logger potrebbe aver bisogno di sapere alcune cose "per magia" affinché funzioni, ma codificare un percorso file nella definizione della classe è potenzialmente un molto più gestibile e meno noioso che trasmetterlo correttamente a centinaia di classi diverse, e probabilmente molto meno malvagio che usare una variabile globale per bypassare detto tedio. (Certo, i logger sono tra i pochissimi casi d'uso legittimi per le variabili globali)


1

Sono d'accordo con quelli che suggeriscono che il logger dovrebbe essere accessibile staticamente piuttosto che passare alle classi. Tuttavia, se c'è una ragione forte per cui vuoi passarlo (forse istanze diverse vogliono accedere a posizioni diverse o qualcosa del genere) allora suggerirei di non passarlo usando il costruttore ma piuttosto di effettuare una chiamata separata per farlo, ad es. Class* C = new C(); C->SetLogger(logger);Piuttosto diClass* C = new C(logger);

Il motivo per preferire questo metodo è che il logger non è essenzialmente una parte della classe ma piuttosto una funzione iniettata utilizzata per qualche altro scopo. Inserirlo nell'elenco dei costruttori lo rende un requisito della classe e implica che fa parte dello stato logico effettivo della classe. E 'ragionevole aspettarsi, per esempio (con la maggior parte le classi anche se non tutti), che se X != Yallora C(X) != C(Y), ma è improbabile che si sarebbe testare logger disuguaglianza se si confrontano anche istanze della stessa classe.


1
Naturalmente questo ha lo svantaggio che il logger non è disponibile per il costruttore.
Ben Voigt,

1
Mi piace molto questa risposta. Rende il logging una preoccupazione secondaria della classe di cui devi preoccuparti separatamente dal solo usarlo. È probabile che se stai aggiungendo il logger al costruttore di un gran numero di classi, probabilmente stai usando l'iniezione di dipendenza. Non posso parlare per tutte le lingue, ma so in C #, alcune implementazioni DI verranno anche iniettate direttamente nelle proprietà (getter / setter).
jpmc26

@BenVoigt: è vero, e potrebbe essere una ragione killer non farlo in questo modo ma, di solito, puoi fare la registrazione che altrimenti faresti nel costruttore in risposta all'impostazione di un logger.
Jack Aidley,

0

Vale la pena menzionare, qualcosa che non ho visto toccare le altre risposte qui, è che rendendo il logger iniettato tramite proprietà o statico, rende difficile (er) testare la classe. Ad esempio, se si effettua l'iniezione del logger tramite la proprietà, sarà necessario iniettare tale logger ogni volta che si verifica un metodo che utilizza il logger. Questo significa che potresti anche averlo impostato come dipendenza del costruttore perché la classe lo richiede.

Statico si presta allo stesso problema; se il logger non funziona, allora l'intera classe fallisce (se la tua classe usa il logger) - anche se il logger non fa necessariamente parte della responsabilità della classe - anche se non è così male come l'iniezione di proprietà perché tu almeno sapere che il logger è sempre "lì" in un certo senso.

Solo un po 'di spunti di riflessione, soprattutto se impieghi TDD. A mio avviso, un logger non dovrebbe davvero far parte di una parte testabile di una classe (quando si verifica la classe, non si dovrebbe testare anche la registrazione).


1
hmmm ... quindi vuoi che la tua classe esegua il logging (il logging dovrebbe essere nelle specifiche) ma non vuoi testare usando un logger. È possibile? Penso che il tuo punto sia un non-go. Chiaramente se i tuoi strumenti di test sono rotti non puoi testarli - progettare in modo da non fare affidamento su uno strumento di test è un po 'troppo ingegneristico di IMHO
Hogan

1
Il mio punto è che se uso un costruttore e chiamo un metodo su una classe e fallisce ancora perché non ho impostato una proprietà, allora il progettista della classe ha frainteso il concetto alla base di un costruttore. Se un logger è richiesto dalla classe, dovrebbe essere iniettato nel costruttore - questo è ciò per cui il costruttore è lì.
Dan Pantry,

umm .. no non proprio. Se si considera il sistema di registrazione parte del "framework", non ha senso come parte del costruttore. Ma questo è stato affermato chiaramente in altre risposte.
Hogan,

1
Sto discutendo contro l'iniezione di proprietà. Non sto necessariamente sostenendo l'uso dell'iniezione nel costruttore. Sto solo dicendo che secondo me è preferibile l'iniezione di proprietà.
Dan Pantry,

"È possibile?" inoltre, sì, la tessitura IL è una cosa esistente ed è stata menzionata nella risposta migliore ... mono-project.com/docs/tools+libraries/libraries/Mono.Cecil
Dan Pantry

0

Sono troppo pigro per passare un oggetto logger a ogni istanza della classe. Quindi, nel mio codice, questo genere di cose si trova in un campo statico o in una variabile thread-local in un campo statico. Quest'ultimo è piuttosto interessante e ti consente di utilizzare un logger diverso per ogni thread e ti consente di aggiungere metodi per accendere e spegnere la registrazione che fanno qualcosa di significativo e atteso in un'applicazione multi-thread.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.