Serilog: Ajout de la prise en charge de la chaîne interpolée

Créé le 18 janv. 2016  ·  5Commentaires  ·  Source: serilog/serilog

Lors de l'utilisation de C# 6.0, il n'est actuellement pas possible d'utiliser des chaînes interpolées pour se connecter.

Prenant l'exemple de la page d'accueil de Serilog

var position = new { Latitude = 25, Longitude = 134 };
var elapsedMs = 34;

log.Information("Processed {@Position} in {Elapsed:000} ms.", position, elapsedMs);

Pour le moment, il n'y a aucun moyen d'utiliser la chaîne interpolée et d'utiliser le sérialiseur de Serilog.

log.Information($"Processed {position} in {elapsedMs:000} ms.");

Ce serait bien de pouvoir utiliser une chaîne interpolée tout en profitant du sérialiseur de Serilog, en utilisant quelque chose comme :

log.Information($"Processed @{position} in @{elapsedMs:000} ms.");

C'est faisable en utilisant des méthodes qui attendent un FormattableString , cependant s'il y a des surcharges prenant également une chaîne, la chaîne sera préférée par le compilateur. Une façon de le faire fonctionner consiste à faire en sorte que toutes les surcharges de chaîne soient des méthodes d'extension au lieu de méthodes d'instance.

J'ai écrit un résumé pour démontrer : https://gist.github.com/Pvlerick/f4d0876b82f85ef369d0

La mise en œuvre est rapide et sale mais ne sert qu'à prouver que c'est faisable. Il pourrait aussi y avoir des moyens de mettre en œuvre cela auxquels je n'avais pas pensé, bien sûr :sourire:

De plus, un problème potentiel est que cela dépend du comportement du compilateur, qui ne sera probablement pas dans les spécifications (j'ai écrit un article de blog à ce sujet avec plus de détails). Cependant, je pense qu'il est possible de rendre l'implémentation suffisamment robuste pour fonctionner dans tous les cas.

Commentaire le plus utile

facile:
log.Information_($"Processed {new {Position = position}} in {new {Elapsed = elapsed}:000} ms.")

J'ai dû l'implémenter en tant qu'extension avec le suffixe "_", car sinon roslyn invoque ILogger.Information(string) place :

    public static class LoggerExtensions
    {
        public class FormattableLogPropValue : IFormattable
        {
            public FormattableLogPropValue(string name, object value)
            {
                this.Name = name;
                this.Value = value;
            }

            public string Name { get; }
            public object Value { get; }

            public bool IsObject() {
                var type = Value?.GetType();
                var res = Type.GetTypeCode(type)==TypeCode.Object;
                return res;
            }

            public string ToString(string format, IFormatProvider formatProvider)
            {
                var prefix = IsObject() ? "@" : string.Empty;
                if (format?.Length > 0) format = ":" + format;
                return $"{{{prefix}{Name}{format}}}";
            }

            public override string ToString()
            {
                return this.ToString(null, null);
            }
        }

        public static void Information_(this ILogger logger, FormattableString str)
        {
            var loggables = str.GetArguments().Select(arg=>GetPropValueFromArgument(logger, arg)??arg).ToArray();

            var messageTemplate = string.Format(str.Format, args: loggables.Select(arg => GetPropValueFromArgument(logger,arg)??arg).ToArray());

            var propertyValues = loggables.OfType<FormattableLogPropValue>().Select(pv=>pv.Value).ToArray();
            logger.Information(messageTemplate, propertyValues: propertyValues);
        }


        static FormattableLogPropValue GetPropValueFromArgument(ILogger logger, object arg)
        {
            var argType = arg.GetType();
            if (!argType.Name.StartsWith("<>f__AnonymousType")) return null;
            var props = argType.GetProperties();
            if (props.Length != 1) throw new ArgumentException("Parameter should only have single property");
            var prop = props.First();
            return new FormattableLogPropValue(prop.Name, prop.GetValue(arg));
        }
    }

Quelqu'un veut-il soumettre un PR?

Tous les 5 commentaires

Je ne sais pas comment le support de FormattableString aiderait beaucoup ici. Du moins pas jusqu'à ce que https://github.com/dotnet/roslyn/issues/142 soit résolu.

Que feriez-vous pour des scénarios comme log.Information($"Processed {Path.Combine(folderPath, fileName)} in {elapsedMs:000} ms."); ?

Aussi, pour référence; Interpolation de chaînes C# 6 et Serilog

@Pvlerick - article de blog soigné ! J'y avais aussi pensé dans le passé et, même avec le billet de blog de

@khellang a raison sur le scénario donné. Une partie de moi pense que "le parfait est l'ennemi du bien" et le problème référencé de Roslyn finira par apporter une solution. En attendant, se connecter avec des espaces réservés factices ne serait pas la fin du monde. L'autre partie de moi s'inquiète que quelque chose soit construit puis se brise à l'avenir. Matière à réflexion :fork_and_knife:

@IanYates merci. Je suis d'accord que c'est une sorte de boîte de vers et comme vous l'avez souligné à juste titre, cela pourrait conduire à quelque chose de fragile qui pourrait se briser à l'avenir.

Je vais continuer à enquêter et voir s'il existe un moyen de faire en sorte que cela fonctionne bien en ce qui concerne les points de @nblumhardt et nous verrons.

facile:
log.Information_($"Processed {new {Position = position}} in {new {Elapsed = elapsed}:000} ms.")

J'ai dû l'implémenter en tant qu'extension avec le suffixe "_", car sinon roslyn invoque ILogger.Information(string) place :

    public static class LoggerExtensions
    {
        public class FormattableLogPropValue : IFormattable
        {
            public FormattableLogPropValue(string name, object value)
            {
                this.Name = name;
                this.Value = value;
            }

            public string Name { get; }
            public object Value { get; }

            public bool IsObject() {
                var type = Value?.GetType();
                var res = Type.GetTypeCode(type)==TypeCode.Object;
                return res;
            }

            public string ToString(string format, IFormatProvider formatProvider)
            {
                var prefix = IsObject() ? "@" : string.Empty;
                if (format?.Length > 0) format = ":" + format;
                return $"{{{prefix}{Name}{format}}}";
            }

            public override string ToString()
            {
                return this.ToString(null, null);
            }
        }

        public static void Information_(this ILogger logger, FormattableString str)
        {
            var loggables = str.GetArguments().Select(arg=>GetPropValueFromArgument(logger, arg)??arg).ToArray();

            var messageTemplate = string.Format(str.Format, args: loggables.Select(arg => GetPropValueFromArgument(logger,arg)??arg).ToArray());

            var propertyValues = loggables.OfType<FormattableLogPropValue>().Select(pv=>pv.Value).ToArray();
            logger.Information(messageTemplate, propertyValues: propertyValues);
        }


        static FormattableLogPropValue GetPropValueFromArgument(ILogger logger, object arg)
        {
            var argType = arg.GetType();
            if (!argType.Name.StartsWith("<>f__AnonymousType")) return null;
            var props = argType.GetProperties();
            if (props.Length != 1) throw new ArgumentException("Parameter should only have single property");
            var prop = props.First();
            return new FormattableLogPropValue(prop.Name, prop.GetValue(arg));
        }
    }

Quelqu'un veut-il soumettre un PR?

Je viens de publier une bibliothèque (avec des extensions à Serilog, NLog et Microsoft ILogger) qui permet d'écrire des messages de journal à l'aide de chaînes interpolées tout en définissant les noms de propriété via différentes syntaxes.
J'aimerais entendre des commentaires.
https://github.com/Drizin/InterpolatedLogging

Cette page vous a été utile?
0 / 5 - 0 notes