C #에서 전체 개체를 로그에 덤프하는 가장 좋은 방법은 무엇입니까?


129

따라서 런타임에 현재 객체의 상태를 보려면 Visual Studio Immediate 창이 제공하는 것이 정말 좋습니다. 간단하게

? objectname

객체의 멋진 '덤프'를 나에게 줄 것입니다.

코드 에서이 작업을 수행하는 쉬운 방법이 있습니까? 그래서 로깅 할 때 비슷한 작업을 수행 할 수 있습니까?


결국, 나는 T.Dump를 꽤 많이 사용했습니다. 매우 견고한 솔루션입니다. 재귀에주의해야합니다.
Dan Esparza

이것은 오래된 질문이지만 많은 검색 히트의 맨 위에 나옵니다. 향후 독자 : 이 기능과 확장 기능을 참조하십시오 . VS2015에서 나를 위해 일했습니다.
Jesse Good

1
VS 플러그인이 유지되지 않고 일부 기능이 없기 때문에 2020에 대한 업데이트. 다음 라이브러리는 코드에서 동일한 작업을 수행하며 몇 가지 추가 기능이 있습니다. 예를 들어 루프를 피하기 위해 이미 방문한 위치를 추적합니다. github.com/thomasgalliker/ObjectDumper
Nick Westgate

답변:


55

Linq 샘플 과 함께 제공되는 ObjectDumper 코드를 기반으로 할 수 있습니다 .
관련 질문 에 대한 답을 살펴보고 샘플을 얻으십시오.


5
또한 배열에서는 작동하지 않습니다 (단지 배열의 유형과 길이 만 표시하지만 내용은 인쇄하지 않습니다).
Konrad Morawski

5
ObjectDumper 용 nuget 패키지를 사용할 수 있습니다. 또한 확장 방법 제공 DumpToStringDumpObject클래스를. 능숙한.
IsmailS

2
w3wp.exe내가 ObjectDumper같은 것을 사용하려고 할 때 충돌Request.DumpToString("aaa");
Paul

60

더 큰 객체 그래프의 경우 Json을 사용하지만 전략이 약간 다릅니다. 먼저 호출하기 쉬운 정적 클래스와 Json 변환을 래핑하는 정적 메소드가 있습니다 (참고 :이를 확장 메소드로 만들 수 있습니다).

using Newtonsoft.Json;

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

그런 다음에 Immediate Window,

var lookHere = F.Dump(myobj);

look Locals앞에 $ 가 붙은 창 에 자동으로 표시 되거나 시계를 추가 할 수 있습니다. Value인스펙터 에서 컬럼 의 오른쪽 에는 드롭 다운 캐럿이있는 돋보기가 옆에 있습니다. 드롭 다운 캐럿을 선택하고 Json Visualizer를 선택하십시오.

Visual Studio 2013 Locals 창의 스크린 샷

Visual Studio 2013을 사용하고 있습니다.


2
SerializeObj-> SerializeObject?
Wiseman

훌륭합니다, 감사합니다 원격 서버에 Visual Studio 용 디버그 도구를 설치할 수 없으며이 기능은 asp.net mvc 앱에서 매우 잘 작동합니다.
Liam Kernighan

1
멋진 서식을 위해 다음을 수행 할 수 있습니다.Newtonsoft.Json.JsonConvert.SerializeObject(sampleData, Formatting.Indented)
Zorgarath

손으로하는 것보다 훨씬 쉽습니다. 그것은 복잡해진다
ahong

26

나는 이것을하는 더 좋은 방법이 있다고 확신하지만, 과거에는 객체를 내가 기록 할 수있는 문자열로 직렬화하기 위해 다음과 같은 방법을 사용했다.

  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;
  }

메소드가 직렬화 된 오브젝트가 아니라 예외를 리턴 할 수도 있으므로 로그하려는 오브젝트가 직렬화 가능한지 확인해야합니다.


2
질문에 대답 한 후 C #에 추가 된 기능에 비추어 볼 때이 구현이 확장 방법으로 훌륭하게 작동한다는 것을 나타내는 것이 유용 할 수 있습니다. Object 클래스에 적용되고 필요한 곳 ​​어디에서나 확장을 참조하면 함수를 호출하는 편리한 방법이 될 수 있습니다.
Nikita G.

나는 이것을 계속 얻는다 : Failed to access type 'System.__ComObject' failed. 멍청한 놈, 도움을 주셔서 감사합니다.
GuySoft

1
@GuySoft 객체의 속성 중 하나 또는 객체 자체가 직렬화 가능하지 않은 것 같습니다.
Bernhard Hofmann

불행히도 매개 변수없는 생성자가없는 클래스에서는이 메서드를 사용할 수 없습니다. 직렬화 할 수 없습니다.
Jarekczek

22

Visual Studio Immediate Window를 사용할 수 있습니다

이것을 붙여 넣으십시오 ( actual명명하게 객체 이름으로 변경 하십시오).

Newtonsoft.Json.JsonConvert.SerializeObject(actual);

JSON으로 객체를 인쇄해야합니다. 여기에 이미지 설명을 입력하십시오

textmechanic 텍스트 도구 또는 notepad ++를 통해 복사하고 이스케이프 된 따옴표 ( \")와 "개행 ( \r\n)을 빈 공간으로 바꾼 다음 큰 따옴표 ( ")를 시작과 끝에서 제거 하고 더 읽기 쉽게 jsbeautifier 에 붙여 넣으십시오 .

OP의 의견에 업데이트

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

그러면 객체를 덤프 할 수 있습니다.

이것이 시간을 절약하기를 바랍니다.


감사. 아마도 당신은 내 원래의 질문에서 그것을 잡지 못했지만, 나는 즉시 창에 대해 이미 알고 있다고 지적했으며 내 앱에 로그인 할 때도 똑같은 일을하고 싶었습니다.
Dan Esparza 2016 년

@DanEsparza Console.Log(Newtonsoft.Json.JsonConvert.SerializeObject(actual));? :) 그리고 그렇습니다 나는 그것을 정말로 놓쳤다. 이 질문은 google.co.uk/…
Matas Vaitkevicius

2
참고로 C # 문자열에 JSON 문자열이 있으면 문자열 오른쪽에있는 망원경 아이콘을 클릭하고 텍스트 시각화 도구를 선택하십시오. JSON 문자열의 일반 텍스트 버전을 표시하는 창을 표시합니다 (이스케이프 따옴표 나 \ r \ n 아님).
Walter

16

ServiceStack.Text 에는 정확하게 이것을 수행 하는 T.Dump () 확장 메소드 가 있으며, 모든 유형의 모든 특성을 읽기 쉬운 형식으로 재귀 적으로 덤프합니다.

사용법 예 :

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

출력 :

{
    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
    }
}

1
필드에서는 작동하지 않습니다. OP는 "전체 개체"에 대해 명시 적으로 요청했습니다.
Konrad Morawski

5
He didn't say fields-그는 entire objects필드를 포함 하여 말했다 . 그는 또한 Visual Studio의 직접 실행 창 기능을 달성하고자하는 것의 예로 언급했습니다 ( "단순한 작업만으로도 ? objectname개체의 '덤프'형식을 지정할 수 있습니다" ). ? objectname모든 필드를 인쇄합니다. This has been immensely helpful - one of my most used extension methods to date-유용하다고 의심하지 않고 전체 객체를 덤프하는 것만 의심합니다.
Konrad Morawski

3
@KonradMorawski 잘못된 전체 객체객체 의 재귀 덤프를 의미하며 필드를 포함하지 않으며 무한 재귀 루프로 쉽게 이어질 수 있습니다. 다른 사람들이 암시하는 것을 가정해서는 안됩니다. 내 대답은 관련이 있고 도움이되며, 귀하의 다운 투표 + 의견은 아닙니다.
mythz

1
@mythz 예 물론 스택 오버플로를 방지해야합니다 (예 : 모든 Int32필드에는 자체 MaxValue필드가 Int32있습니다 ...).하지만 좋은 지적이지만 객체와 확실히 전체 객체를 변경하지는 않습니다. -속성뿐만 아니라 필드로 구성됩니다. 무엇보다, (하나 그 주소하지 않았다) ? objectname에서 Immediate Window 않는 무한 루프를 트리거하지 않고 - 디스플레이 분야. 그것이 나의 공감에 관한 것이라면, 나는 그것을 철회 할 수 있습니다 (잠금을 해제하여 알려 주면, 즉). 어쨌든 원칙적으로 동의하지 않습니다.
Konrad Morawski

4
링크 전용 답변의 경우 -1이지만 사용할 수 있으면 멋지게 보입니다! 아마 나는 장님이지만, 그 링크를 통해 소스를 찾을 수 없습니다. 두 개의 업로드 폴더가 비어 있습니다. 코드가 너무 길어서 답변에 포함되지 않습니까?

14

다음은 깔끔한 형식의 평면 객체를 작성하는 어리석은 간단한 방법입니다.

using Newtonsoft.Json.Linq;

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

계속해서 객체는 먼저로 내부 JSON 표현으로 JObject.FromObject변환 된 다음로로 JSON 문자열로 변환됩니다 ToString. (물론 JSON 문자열은 간단한 객체를 아주 잘 표현한 것입니다. 특히 줄 ToString바꿈과 들여 쓰기가 포함되어 있기 때문 입니다.) "ToString"은 물론 ( +문자열과 객체를 연결하기 위해을 사용하여 암시되기 때문에) 외부 적입니다. 여기에 지정하고 싶습니다.


5
로그에 편안한 독서를위한 JsonConvert.SerializeObject (안목, Formatting.Indented)
Tertium

1
HotLicks-지금이 공헌이 얼마나 중요한지 알려 드리고자합니다. 업데이트 중에 변경된 사항에 대한 감사를 제공해야하며 '공황'수준에서 관리 가능한 '걱정'수준으로 스트레스를 받았습니다. 감사합니다. 매우 길고 축복받은 삶을 살 수
있습니까?

4

리플렉션을 사용하고 모든 객체 속성을 반복 한 다음 해당 값을 가져 와서 로그에 저장할 수 있습니다. 형식은 실제로 사소합니다 (\ t를 사용하여 객체 속성과 해당 값을 들여 쓸 수 있습니다).

MyObject
    Property1 = value
    Property2 = value2
    OtherObject
       OtherProperty = value ...

4

내가 좋아하는 것은 ToString ()을 재정 의하여 형식 이름보다 더 유용한 출력을 얻는 것입니다. 이것은 디버거에서 편리하며 객체를 확장하지 않고도 객체에 대한 정보를 볼 수 있습니다.


3

객체 및 컬렉션을 문자열 (및 기타)로 쉽게 덤프 할 수있는 ObjectPrinter 라는 라이브러리를 발견 했습니다. 내가 필요한 것을 정확하게 수행합니다.


3

다음은 동일한 작업을 수행하고 중첩 속성을 처리하는 다른 버전으로, 더 간단하다고 생각합니다 (외부 라이브러리에 대한 종속성이 없으며 로깅 이외의 작업을 수행하기 위해 쉽게 수정할 수 있음).

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"));
    }
}

1
당신이 Date당신의 내면의 물건에 재산 을 가지고 있다면 이것은 끔찍하게 죽을 것입니다 ... 그냥 ...
Noctis

2

자신 만의 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());
    }

다음과 같이 사용하십시오.

WriteLine(myObject);

컬렉션을 작성하려면

 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);
            }
        }   

방법은 다음과 같습니다.

 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());
        }
    }

이런 방식으로 if, else if인터페이스, 속성, 기본 유형 등 및 재귀를 사용 하고 확인 하면 (재귀 적 방법이므로) 객체 덤프를 달성 할 수 있지만 지루합니다. Microsoft LINQ Sample의 객체 덤프를 사용하면 시간이 절약됩니다.


궁금한 점 : 배열 또는 목록을 어떻게 처리합니까? 아니면 부모 객체를 참조하는 속성?
Dan Esparza

@DanEsparza 좀 더 구체적으로 설명해 주셔서 감사합니다.
Ariful Islam

2

@engineforce 답변을 기반으로 Xamarin 솔루션의 PCL 프로젝트에서 사용중인이 클래스를 만들었습니다.

/// <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;
    }
}

0

위의 모든 경로는 객체가 XML 또는 JSON으로 직렬화 가능하거나
자체 솔루션을 구현해야 한다고 가정합니다 .

그러나 결국 당신은 여전히 ​​같은 문제를 해결해야 할 시점에 도달합니다.

  • 객체의 재귀
  • 직렬화 할 수없는 객체
  • 예외
  • ...

또한 로그는 더 많은 정보를 원합니다.

  • 이벤트가 발생했을 때
  • 콜 스택
  • 어느 삼원
  • 웹 세션에 있었던 것
  • 어떤 IP 주소
  • url
  • ...

이 모든 것을 해결하는 최상의 솔루션이 있습니다.
이 Nuget 패키지를 사용하십시오 : Desharp .
모두 - 모든 유형의 애플리케이션에 대한 웹 및 데스크톱 응용 프로그램 . Desharp Github 문서를
참조하십시오 . 그것은이 많은 구성 옵션을 .

어디서나 전화하세요 :

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
  • 멋진 HTML (또는 TEXT 형식, 구성 가능)로 로그를 저장할 수 있습니다
  • 백그라운드 스레드에서 선택적으로 쓸 수 있습니다 (구성 가능)
  • 최대 객체 깊이 및 최대 문자열 길이에 대한 옵션이 있습니다 (구성 가능).
  • 그것은 iteratable 객체와 다른 모든 것들에 대한 역 반사에 대한 루프를 사용
    하기위한 실제로 는 .NET 환경에서 찾을 수있는 일 .

도움이 될 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.