Qual é a melhor maneira de despejar objetos inteiros em um log em C #?

129

Portanto, para visualizar o estado atual de um objeto em tempo de execução, gosto muito do que a janela Imediata do Visual Studio me fornece. Apenas fazendo um simples

? objectname

Vai me dar um 'despejo' bem formatado do objeto.

Existe uma maneira fácil de fazer isso no código, para que eu possa fazer algo semelhante ao fazer logon?

Dan Esparza
fonte
No final, eu usei o T.Dump bastante. É uma solução bastante sólida - você só precisa ter cuidado com a recursão.
Dan Esparza
Essa é uma pergunta antiga, mas aparece no topo de muitos hits de pesquisa. Para futuros leitores: Veja esta vs extensão . Funcionou muito bem para mim no VS2015.
Jesse Good
1
Atualização para 2020, pois esse plug-in do VS não é mantido e carece de alguns recursos. A biblioteca a seguir faz a mesma coisa no código - e possui alguns recursos extras, por exemplo, rastreia onde já foi visitado para evitar loops: github.com/thomasgalliker/ObjectDumper
Nick Westgate

Respostas:

55

Você pode basear algo no código ObjectDumper que acompanha as amostras do Linq .
Veja também a resposta desta pergunta relacionada para obter uma amostra.

Mike Scott
fonte
5
Também não funciona para matrizes (apenas exibe o tipo e o comprimento da matriz, mas não imprime seu conteúdo).
21912 Konrad Morawski
5
pacote nuget para ObjectDumper já está disponível. Ele também fornece um método de extensão DumpToStringe Dumppara a Objectclasse. Handy.
IsmailS
2
w3wp.exetrava quando tento usar o ObjectDumpertipo #Request.DumpToString("aaa");
Paul
60

Para um gráfico maior de objetos, eu recomendo o uso do Json, mas com uma estratégia ligeiramente diferente. Primeiro, tenho uma classe estática que é fácil de chamar e com um método estático que envolve a conversão do Json (nota: isso pode ser um método de extensão).

using Newtonsoft.Json;

public static class F
{
    public static string Dump(object obj)
    {
        return JsonConvert.SerializeObject(obj);
    }
}

Então no seu Immediate Window,

var lookHere = F.Dump(myobj);

O lookHere será exibido automaticamente na Localsjanela anexada com um $ ou você poderá adicionar um relógio a ele. No lado direito da Valuecoluna no inspetor, há uma lente de aumento com um cursor pendente ao lado. Escolha o cursor suspenso e escolha o visualizador Json.

Captura de tela da janela Locals do Visual Studio 2013

Estou usando o Visual Studio 2013.

Jason
fonte
2
SerializeObj -> SerializeObject?
Wiseman
Brilhante, obrigado. Não consigo instalar ferramentas de depuração para o Visual Studio no meu servidor remoto, e isso funciona muito bem no meu aplicativo asp.net mvc.
Liam Kernighan
1
Para bom formatação que você pode fazer:Newtonsoft.Json.JsonConvert.SerializeObject(sampleData, Formatting.Indented)
Zorgarath
muito mais fácil do que tentar fazê-lo manualmente. Torna-se complicado #
211719
26

Estou certo de que existem maneiras melhores de fazer isso, mas no passado eu usei um método parecido com o seguinte para serializar um objeto em uma string que eu possa registrar:

  private string ObjectToXml(object output)
  {
     string objectAsXmlString;

     System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(output.GetType());
     using (System.IO.StringWriter sw = new System.IO.StringWriter())
     {
        try
        {
           xs.Serialize(sw, output);
           objectAsXmlString = sw.ToString();
        }
        catch (Exception ex)
        {
           objectAsXmlString = ex.ToString();
        }
     }

     return objectAsXmlString;
  }

Você verá que o método também pode retornar a exceção, e não o objeto serializado, portanto, você deve garantir que os objetos que deseja registrar sejam serializáveis.

Bernhard Hofmann
fonte
2
À luz dos recursos adicionados ao C # após a resposta, a pergunta pode ser útil para ressaltar que essa implementação funciona bem como um método de extensão. Se aplicada à classe Object e você fizer referência à extensão em todos os locais que precisar, pode ser uma maneira conveniente de chamar a função.
Nikita G.
Eu continuo recebendo a partir desta: Failed to access type 'System.__ComObject' failed. Noob para c #, gostaria de receber ajuda.
18714 GuySoft
1
@GuySoft Suspeito que uma das propriedades do seu objeto, ou o próprio objeto, não seja serializável.
Bernhard Hofmann
Infelizmente, você não pode usar esse método em classes sem um construtor sem parâmetros. Eles não são serializáveis.
Jarekczek
22

Você pode usar a janela imediata do Visual Studio

Basta colar isto (mude actualpara o nome do seu objeto obviamente):

Newtonsoft.Json.JsonConvert.SerializeObject(actual);

Ele deve imprimir o objeto em JSON insira a descrição da imagem aqui

Você deve poder copiá-lo sobre a ferramenta de texto mecânico ou o bloco de notas ++ e substituir aspas escapadas ( \") por "e newlines ( \r\n) por espaço vazio, remover aspas duplas ( ") do início e final e colá-las no jsbeautifier para torná-las mais legíveis.

ATUALIZAR no comentário do OP

public static class Dumper
{
    public static void Dump(this object obj)
    {
        Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(obj)); // your logger
    }
}

isso deve permitir que você despeje qualquer objeto.

Espero que isso poupe algum tempo.

Matas Vaitkevicius
fonte
Obrigado. Talvez você não tenha entendido na minha pergunta original, mas indiquei que já sabia da janela imediata e queria fazer a mesma coisa ao fazer login no meu aplicativo.
Dan Esparza
@DanEsparza Console.Log(Newtonsoft.Json.JsonConvert.SerializeObject(actual));? :) e sim, eu realmente senti falta. Esta questão surge quando você pesquisa google.co.uk/…
Matas Vaitkevicius
2
Para sua informação, quando você tem uma string JSON em uma string C #, clique no ícone luneta à direita da string e escolha o Visualizador de Texto. Ele exibirá uma janela que mostra uma versão em texto sem formatação da string JSON (sem aspas ou \ r \ n).
Walter
16

O ServiceStack.Text possui um método de extensão T.Dump () que faz exatamente isso, despeja recursivamente todas as propriedades de qualquer tipo em um bom formato legível.

Exemplo de uso:

var model = new TestModel();
Console.WriteLine(model.Dump());

e saída:

{
    Int: 1,
    String: One,
    DateTime: 2010-04-11,
    Guid: c050437f6fcd46be9b2d0806a0860b3e,
    EmptyIntList: [],
    IntList:
    [
        1,
        2,
        3
    ],
    StringList:
    [
        one,
        two,
        three
    ],
    StringIntMap:
    {
        a: 1,
        b: 2,
        c: 3
    }
}
mythz
fonte
1
Não funciona para campos. O OP estava perguntando explicitamente sobre "objetos inteiros".
21912 Konrad Morawski
5
He didn't say fields- ele disse entire objects, que inclui campos. Ele também mencionou o recurso Immediate Window do Visual Studio como um exemplo do que ele queria alcançar ( "Apenas fazer um simples ? objectnameme dará um 'despejo' bem formatado do objeto" ). ? objectnameimprime todos os campos também. This has been immensely helpful - one of my most used extension methods to date- Não estou questionando se é útil, apenas que despeja objetos inteiros.
Konrad Morawski
3
@KonradMorawski Objetos inteiros errados significam um despejo recursivo do objeto, NÃO que inclua campos, que podem facilmente levar a um loop recursivo infinito. Você não deve assumir o que os outros estão implicando. Minha resposta é relevante e útil, seu voto negativo + comentário não são.
mythz 20/09/12
1
@mythz sim, é claro que você precisa evitar um estouro de pilha (por exemplo, todo Int32campo tem um MaxValuecampo, que é um Int32...), esse é um bom ponto, mas não altera o fato de que objetos - e certamente objetos inteiros - consistem em campos também, não apenas propriedades. O que é mais (você não fez endereço que um), ? objectnamenos Immediate Window faz campos de exibição - sem disparar um loop infinito. Se isso é sobre o meu voto negativo, posso retirá-lo (se você me permitir desbloqueá-lo). Eu discordo em princípio de qualquer maneira.
21912 Konrad Morawski
4
-1 para essencialmente uma resposta apenas de link, embora pareça ótimo se eu pudesse usá-lo! Talvez eu seja cego, mas não consigo encontrar a fonte através desse link; as duas pastas de upload estão vazias. O código é longo demais para ser incluído na resposta?
14

Aqui está uma maneira estupidamente simples de escrever um objeto plano, bem formatado:

using Newtonsoft.Json.Linq;

Debug.WriteLine("The object is " + JObject.FromObject(theObjectToDump).ToString());

O que está acontecendo é que o objeto é primeiro convertido em uma representação interna JSON por JObject.FromObjecte, em seguida, convertido em string JSON por ToString. (E, é claro, uma string JSON é uma representação muito boa de um objeto simples, especialmente porque ToStringincluirá novas linhas e recuos.) O "ToString" é obviamente estranho (como está implícito no uso +para concaturar uma string e um objeto), mas Eu meio que gosto de especificar aqui.

Hot Licks
fonte
5
JsonConvert.SerializeObject (apprec, Formatting.Indented) para a leitura confortável em log
Tertium
1
HotLicks - Quero transmitir a você o quanto essa contribuição é importante para mim no momento. Eu tenho um requisito para fornecer uma auditoria do que mudou durante uma atualização e você acabou de aliviar meu estresse do nível de 'pânico' para um nível gerenciável de 'preocupação'. Obrigado senhor, pode você tem uma vida muito longa e abençoada
Iofacture
4

Você pode usar a reflexão e percorrer todas as propriedades do objeto, obter seus valores e salvá-los no log. A formatação é realmente trivial (você pode usar \ t para indentar as propriedades de um objeto e seus valores):

MyObject
    Property1 = value
    Property2 = value2
    OtherObject
       OtherProperty = value ...
Ricardo Villamil
fonte
4

O que eu gosto de fazer é substituir o ToString () para obter uma saída mais útil além do nome do tipo. Isso é útil no depurador, você pode ver as informações que deseja sobre um objeto sem precisar expandi-lo.

Darryl Braaten
fonte
3

Eu encontrei uma biblioteca chamada ObjectPrinter que permite despejar facilmente objetos e coleções em strings (e mais). Faz exatamente o que eu precisava.

Marek Dzikiewicz
fonte
3

A seguir, está outra versão que faz a mesma coisa (e lida com propriedades aninhadas), que eu acho que é mais simples (sem dependências de bibliotecas externas e pode ser modificada facilmente para fazer outras coisas além do log):

public class ObjectDumper
{
    public static string Dump(object obj)
    {
        return new ObjectDumper().DumpObject(obj);
    }

    StringBuilder _dumpBuilder = new StringBuilder();

    string DumpObject(object obj)
    {
        DumpObject(obj, 0);
        return _dumpBuilder.ToString();
    }

    void DumpObject(object obj, int nestingLevel = 0)
    {
        var nestingSpaces = "".PadLeft(nestingLevel * 4);

        if (obj == null)
        {
            _dumpBuilder.AppendFormat("{0}null\n", nestingSpaces);
        }
        else if (obj is string || obj.GetType().IsPrimitive)
        {
            _dumpBuilder.AppendFormat("{0}{1}\n", nestingSpaces, obj);
        }
        else if (ImplementsDictionary(obj.GetType()))
        {
            using (var e = ((dynamic)obj).GetEnumerator())
            {
                var enumerator = (IEnumerator)e;
                while (enumerator.MoveNext())
                {
                    dynamic p = enumerator.Current;

                    var key = p.Key;
                    var value = p.Value;
                    _dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, key, value != null ? value.GetType().ToString() : "<null>");
                    DumpObject(value, nestingLevel + 1);
                }
            }
        }
        else if (obj is IEnumerable)
        {
            foreach (dynamic p in obj as IEnumerable)
            {
                DumpObject(p, nestingLevel);
            }
        }
        else
        {
            foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(obj))
            {
                string name = descriptor.Name;
                object value = descriptor.GetValue(obj);

                _dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, name, value != null ? value.GetType().ToString() : "<null>");
                DumpObject(value, nestingLevel + 1);
            }
        }
    }

    bool ImplementsDictionary(Type t)
    {
        return t.GetInterfaces().Any(i => i.Name.Contains("IDictionary"));
    }
}
engineforce
fonte
1
este vai morrer horrivelmente se você tem uma Datepropriedade em seu objeto interno ... apenas dizendo ...
Noctis
2

Você pode escrever seu próprio método WriteLine-

public static void WriteLine<T>(T obj)
    {
        var t = typeof(T);
        var props = t.GetProperties();
        StringBuilder sb = new StringBuilder();
        foreach (var item in props)
        {
            sb.Append($"{item.Name}:{item.GetValue(obj,null)}; ");
        }
        sb.AppendLine();
        Console.WriteLine(sb.ToString());
    }

Use-o como-

WriteLine(myObject);

Para escrever uma coleção, podemos usar-

 var ifaces = t.GetInterfaces();
        if (ifaces.Any(o => o.Name.StartsWith("ICollection")))
        {

            dynamic lst = t.GetMethod("GetEnumerator").Invoke(obj, null);
            while (lst.MoveNext())
            {
                WriteLine(lst.Current);
            }
        }   

O método pode parecer

 public static void WriteLine<T>(T obj)
    {
        var t = typeof(T);
        var ifaces = t.GetInterfaces();
        if (ifaces.Any(o => o.Name.StartsWith("ICollection")))
        {

            dynamic lst = t.GetMethod("GetEnumerator").Invoke(obj, null);
            while (lst.MoveNext())
            {
                WriteLine(lst.Current);
            }
        }            
        else if (t.GetProperties().Any())
        {
            var props = t.GetProperties();
            StringBuilder sb = new StringBuilder();
            foreach (var item in props)
            {
                sb.Append($"{item.Name}:{item.GetValue(obj, null)}; ");
            }
            sb.AppendLine();
            Console.WriteLine(sb.ToString());
        }
    }

Usando if, else ife verificando interfaces, atributos, tipo de base, etc. e recursão (como esse é um método recursivo), dessa maneira, podemos obter um descarregador de objetos, mas é tedioso, com certeza. Usar o descarregador de objetos do LINQ Sample da Microsoft economizaria seu tempo.

Ariful Islam
fonte
Por curiosidade: como isso lida com matrizes ou listas? Ou propriedades que fazem referência a objetos pai?
Dan Esparza
@ DanEsparza Obrigado por me mostrar o caminho para ser mais específico.
Ariful Islam
2

Com base na resposta do @engineforce, fiz esta classe que estou usando em um projeto PCL de uma solução Xamarin:

/// <summary>
/// Based on: https://stackoverflow.com/a/42264037/6155481
/// </summary>
public class ObjectDumper
{
    public static string Dump(object obj)
    {
        return new ObjectDumper().DumpObject(obj);
    }

    StringBuilder _dumpBuilder = new StringBuilder();

    string DumpObject(object obj)
    {
        DumpObject(obj, 0);
        return _dumpBuilder.ToString();
    }

    void DumpObject(object obj, int nestingLevel)
    {
        var nestingSpaces = "".PadLeft(nestingLevel * 4);

        if (obj == null)
        {
            _dumpBuilder.AppendFormat("{0}null\n", nestingSpaces);
        }
        else if (obj is string || obj.GetType().GetTypeInfo().IsPrimitive || obj.GetType().GetTypeInfo().IsEnum)
        {
            _dumpBuilder.AppendFormat("{0}{1}\n", nestingSpaces, obj);
        }
        else if (ImplementsDictionary(obj.GetType()))
        {
            using (var e = ((dynamic)obj).GetEnumerator())
            {
                var enumerator = (IEnumerator)e;
                while (enumerator.MoveNext())
                {
                    dynamic p = enumerator.Current;

                    var key = p.Key;
                    var value = p.Value;
                    _dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, key, value != null ? value.GetType().ToString() : "<null>");
                    DumpObject(value, nestingLevel + 1);
                }
            }
        }
        else if (obj is IEnumerable)
        {
            foreach (dynamic p in obj as IEnumerable)
            {
                DumpObject(p, nestingLevel);
            }
        }
        else
        {
            foreach (PropertyInfo descriptor in obj.GetType().GetRuntimeProperties())
            {
                string name = descriptor.Name;
                object value = descriptor.GetValue(obj);

                _dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, name, value != null ? value.GetType().ToString() : "<null>");

                // TODO: Prevent recursion due to circular reference
                if (name == "Self" && HasBaseType(obj.GetType(), "NSObject"))
                {
                    // In ObjC I need to break the recursion when I find the Self property
                    // otherwise it will be an infinite recursion
                    Console.WriteLine($"Found Self! {obj.GetType()}");
                }
                else
                {
                    DumpObject(value, nestingLevel + 1);
                }
            }
        }
    }

    bool HasBaseType(Type type, string baseTypeName)
    {
        if (type == null) return false;

        string typeName = type.Name;

        if (baseTypeName == typeName) return true;

        return HasBaseType(type.GetTypeInfo().BaseType, baseTypeName);
    }

    bool ImplementsDictionary(Type t)
    {
        return t is IDictionary;
    }
}
gianlucaparadise
fonte
0

Todos os caminhos acima assumem que seus objetos são serializáveis ​​para XML ou JSON
ou você deve implementar sua própria solução.

Mas no final, você ainda chega ao ponto em que precisa resolver problemas como

  • recursão em objetos
  • objetos não serializáveis
  • exceções
  • ...

Além disso, você deseja obter mais informações:

  • quando o evento aconteceu
  • pilha de chamada
  • que passo
  • o que estava na sessão da web
  • qual endereço ip
  • url
  • ...

Existe a melhor solução que resolve tudo isso e muito mais.
Use este pacote Nuget: Desharp .
Para todos os tipos de aplicativos - aplicativos da Web e de desktop .
Veja a documentação do Desharp Github . Tem muitas opções de configuração .

Basta ligar para qualquer lugar:

Desharp.Debug.Log(anyException);
Desharp.Debug.Log(anyCustomValueObject);
Desharp.Debug.Log(anyNonserializableObject);
Desharp.Debug.Log(anyFunc);
Desharp.Debug.Log(anyFunc, Desharp.Level.EMERGENCY); // you can store into different files
  • ele pode salvar o log em bom HTML (ou no formato TEXT, configurável)
  • é possível escrever opcionalmente no thread de segundo plano (configurável)
  • possui opções para profundidade máxima de objetos e comprimento máximo de strings (configurável)
  • ele usa loops para objetos iteráveis ​​e reflexão para todo o resto, de
    fato , para qualquer coisa que você possa encontrar no ambiente .NET .

Eu acredito que isso vai ajudar.

Tom Flídr
fonte