noxi雑記

.NET、Angularまわりの小ネタブログ

Xamarin.FormsのPrismエラーログを表示する

ViewModelのコンストラクタ間違っている、XAMLが間違っている、ページがDIに登録されていないなど、Xamarin.Formsは様々な理由で開発時にクラッシュします。わりかしサイレントに、こんなログだけ表示して落ちます。

2018-04-28 21:53:38.863 DependencyApp.iOS[56817:1188111] *** Assertion failure in -[UIApplication _runWithMainScene:transitionContext:completion:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3698.52.10/UIApplication.m:3538
Unhandled Exception:

なんの参考にもなりませんね。。

Loggerを設定する

Prismには ILoggerFacade インターフェースを通じてログを出力する機能があります。Prism 6.3ではアプリケーションクラスにこのインターフェースに対応するクラスを返すprotectedなメソッドがありオーバーライドする形式だったのですが、Prism 7.0からはIContainerRegistryに直接登録する方式に変わった様です。なので以下の様に、 RegisterRequiredTypes または RegisterTypes のどちらかで登録します。

protected override void RegisterRequiredTypes(IContainerRegistry containerRegistry)
{
    base.RegisterRequiredTypes(containerRegistry);

    // Prismのログをデバッグログに出力する
    containerRegistry.RegisterSingleton<ILoggerFacade, DebugLogger>();
}

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    // Prismのログをデバッグログに出力する
    containerRegistry.RegisterSingleton<ILoggerFacade, DebugLogger>();

    containerRegistry.RegisterForNavigation<NavigationPage>();
    containerRegistry.RegisterForNavigation<MainPage>();
}

ここで登録しているDebugLoggerはPrismが標準で実装しているログクラスで、名前の通り、デバッグログにログを出力します。この一行を追加することで、エラーログが分かりやすくなります。

BEFORE

2018-04-28 21:53:38.863 DependencyApp.iOS[56817:1188111] *** Assertion failure in -[UIApplication _runWithMainScene:transitionContext:completion:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3698.52.10/UIApplication.m:3538
Unhandled Exception:

AFTER

[0:] EXCEPTION: Unity.Exceptions.ResolutionFailedException: Resolution of the dependency failed, type = 'DependencyApp.ViewModels.MainPageViewModel', name = '(none)'.
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The current type, DependencyApp.IHoge, is an interface and cannot be constructed. Are you missing a type mapping?
-----------------------------------------------
At the time of the exception, the container was: 
  Resolving DependencyApp.ViewModels.MainPageViewModel,(none)
  Resolving parameter 'hoge' of constructor DependencyApp.ViewModels.MainPageViewModel(Prism.Navigation.INavigationService navigationService, DependencyApp.IHoge hoge)
    Resolving DependencyApp.IHoge,(none)
 ---> System.InvalidOperationException: The current type, DependencyApp.IHoge, is an interface and cannot be constructed. Are you missing a type mapping?
  at Unity.ObjectBuilder.BuildPlan.DynamicMethod.DynamicMethodConstructorStrategy.ThrowForAttemptingToConstructInterface (Unity.Builder.IBuilderContext context) [0x00000] in C:\projects\unity\Container\src\ObjectBuilder\BuildPlan\DynamicMethod\DynamicMethodConstructorStrategy.cs:282 
  at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/mcs/class/corlib/System.Reflection/MonoMethod.cs:305 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:152 
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Exception source) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:156 
  at System.Linq.Expressions.Interpreter.ExceptionHelpers.UnwrapAndRethrow (System.Reflection.TargetInvocationException exception) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/Utilities.cs:172 
  at System.Linq.Expressions.Interpreter.MethodInfoCallInstruction.Run (System.Linq.Expressions.Interpreter.InterpretedFrame frame) [0x00035] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/CallInstruction.cs:327 
  at System.Linq.Expressions.Interpreter.Interpreter.Run (System.Linq.Expressions.Interpreter.InterpretedFrame frame) [0x00015] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/Interpreter.cs:63 
  at System.Linq.Expressions.Interpreter.LightLambda.Run1[T0,TRet] (T0 arg0) [0x0001c] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightLambda.Generated.cs:53 
  at Unity.ObjectBuilder.BuildPlan.DynamicMethod.DynamicBuildPlanGenerationContext+<>c__DisplayClass16_0.<GetBuildMethod>b__0 (Unity.Builder.IBuilderContext context) [0x00000] in C:\projects\unity\Container\src\ObjectBuilder\BuildPlan\DynamicMethod\DynamicBuildPlanGenerationContext.cs:134 
  at Unity.ObjectBuilder.BuildPlan.DynamicMethod.DynamicMethodBuildPlan.BuildUp (Unity.Builder.IBuilderContext context) [0x00000] in C:\projects\unity\Container\src\ObjectBuilder\BuildPlan\DynamicMethod\DynamicMethodBuildPlan.cs:36 
  at Unity.ObjectBuilder.Strategies.BuildPlanStrategy.PreBuildUp (Unity.Builder.IBuilderContext context) [0x00056] in C:\projects\unity\Container\src\ObjectBuilder\Strategies\BuildPlanStrategy.cs:36 
  at Unity.Container.StrategyChain.BuildUp (Unity.Builder.IBuilderContext builderContext) [0x00062] in C:\projects\unity\Container\src\Container\StrategyChain.cs:54 
  at Unity.Policy.BuildPlanPolicyExtensions.ExecuteBuildUp (Unity.Policy.IBuildPlanPolicy policy, Unity.Builder.IBuilderContext context) [0x00000] in C:\projects\unity\Abstractions\src\Policy\IBuildPlanPolicy.cs:35 
  at Unity.ObjectBuilder.BuilderContext.NewBuildUp (System.Type type, System.String name, System.Action`1[T] childCustomizationBlock) [0x0004c] in C:\projects\unity\Container\src\ObjectBuilder\BuilderContext.cs:257 
  at Unity.ResolverPolicy.NamedTypeDependencyResolverPolicy.Resolve (Unity.Builder.IBuilderContext context) [0x00000] in C:\projects\unity\Abstractions\src\ResolverPolicy\NamedTypeDependencyResolverPolicy.cs:35 
  at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/mcs/class/corlib/System.Reflection/MonoMethod.cs:305 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:152 
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Exception source) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:156 
  at System.Linq.Expressions.Interpreter.ExceptionHelpers.UnwrapAndRethrow (System.Reflection.TargetInvocationException exception) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/Utilities.cs:172 
  at System.Linq.Expressions.Interpreter.MethodInfoCallInstruction.Run (System.Linq.Expressions.Interpreter.InterpretedFrame frame) [0x00083] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/CallInstruction.cs:352 
  at System.Linq.Expressions.Interpreter.Interpreter.Run (System.Linq.Expressions.Interpreter.InterpretedFrame frame) [0x00015] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/Interpreter.cs:63 
  at System.Linq.Expressions.Interpreter.LightLambda.Run1[T0,TRet] (T0 arg0) [0x0001c] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightLambda.Generated.cs:53 
  at Unity.ObjectBuilder.BuildPlan.DynamicMethod.DynamicBuildPlanGenerationContext+<>c__DisplayClass16_0.<GetBuildMethod>b__0 (Unity.Builder.IBuilderContext context) [0x00000] in C:\projects\unity\Container\src\ObjectBuilder\BuildPlan\DynamicMethod\DynamicBuildPlanGenerationContext.cs:134 
  at Unity.ObjectBuilder.BuildPlan.DynamicMethod.DynamicMethodBuildPlan.BuildUp (Unity.Builder.IBuilderContext context) [0x00000] in C:\projects\unity\Container\src\ObjectBuilder\BuildPlan\DynamicMethod\DynamicMethodBuildPlan.cs:36 
  at Unity.ObjectBuilder.Strategies.BuildPlanStrategy.PreBuildUp (Unity.Builder.IBuilderContext context) [0x00056] in C:\projects\unity\Container\src\ObjectBuilder\Strategies\BuildPlanStrategy.cs:36 
  at Unity.Container.StrategyChain.BuildUp (Unity.Builder.IBuilderContext builderContext) [0x00062] in C:\projects\unity\Container\src\Container\StrategyChain.cs:54 
  at Unity.Policy.BuildPlanPolicyExtensions.ExecuteBuildUp (Unity.Policy.IBuildPlanPolicy policy, Unity.Builder.IBuilderContext context) [0x00000] in C:\projects\unity\Abstractions\src\Policy\IBuildPlanPolicy.cs:35 
  at Unity.UnityContainer.BuildUp (System.Type typeToBuild, System.Object existing, System.String name, Unity.Resolution.ResolverOverride[] resolverOverrides) [0x0007d] in C:\projects\unity\Container\src\UnityContainer.cs:215 
   --- End of inner exception stack trace ---
  at Unity.UnityContainer.BuildUp (System.Type typeToBuild, System.Object existing, System.String name, Unity.Resolution.ResolverOverride[] resolverOverrides) [0x0008d] in C:\projects\unity\Container\src\UnityContainer.cs:219 
  at Unity.UnityContainer.Resolve (System.Type type, System.String name, Unity.Resolution.ResolverOverride[] resolverOverrides) [0x00000] in C:\projects\unity\Container\src\UnityContainer.cs:167 
  at Unity.UnityContainerExtensions.Resolve (Unity.IUnityContainer container, System.Type t, Unity.Resolution.ResolverOverride[] overrides) [0x00000] in C:\projects\unity\Abstractions\src\Utility\UnityContainerExtensions.cs:627 
  at Prism.Unity.UnityContainerExtension.ResolveViewModelForView (System.Object view, System.Type viewModelType) [0x0002a] in <291a0dd6fcb54182bdc253661564be6f>:0 
  at Prism.PrismApplicationBase.<ConfigureViewModelLocator>b__16_0 (System.Object view, System.Type type) [0x00000] in <039c2eaa586846c2bf4ee93ab79399dc>:0 
  at Prism.Mvvm.ViewModelLocationProvider.AutoWireViewModelChanged (System.Object view, System.Action`2[T1,T2] setDataContextCallback) [0x0004e] in <6664c3f36390428490bd163acf505c94>:0 
  at Prism.Mvvm.ViewModelLocator.OnAutowireViewModelChanged (Xamarin.Forms.BindableObject bindable, System.Object oldValue, System.Object newValue) [0x00019] in <039c2eaa586846c2bf4ee93ab79399dc>:0 
  at Xamarin.Forms.BindableObject.SetValueActual (Xamarin.Forms.BindableProperty property, Xamarin.Forms.BindableObject+BindablePropertyContext context, System.Object value, System.Boolean currentlyApplying, Xamarin.Forms.Internals.SetValueFlags attributes, System.Boolean silent) [0x0011b] in D:\agent\_work\1\s\Xamarin.Forms.Core\BindableObject.cs:597 
  at Xamarin.Forms.BindableObject.SetValueCore (Xamarin.Forms.BindableProperty property, System.Object value, Xamarin.Forms.Internals.SetValueFlags attributes, Xamarin.Forms.BindableObject+SetValuePrivateFlags privateAttributes) [0x0015b] in D:\agent\_work\1\s\Xamarin.Forms.Core\BindableObject.cs:391 
  at Xamarin.Forms.BindableObject.SetValue (Xamarin.Forms.BindableProperty property, System.Object value, System.Boolean fromStyle, System.Boolean checkAccess) [0x0005f] in D:\agent\_work\1\s\Xamarin.Forms.Core\BindableObject.cs:544 
  at Xamarin.Forms.BindableObject.SetValue (Xamarin.Forms.BindableProperty property, System.Object value) [0x00000] in D:\agent\_work\1\s\Xamarin.Forms.Core\BindableObject.cs:84 
  at Prism.Mvvm.ViewModelLocator.SetAutowireViewModel (Xamarin.Forms.BindableObject bindable, System.Nullable`1[T] value) [0x00000] in <039c2eaa586846c2bf4ee93ab79399dc>:0 
  at Prism.Common.PageUtilities.SetAutowireViewModelOnPage (Xamarin.Forms.Page page) [0x00017] in <039c2eaa586846c2bf4ee93ab79399dc>:0 
  at Prism.Navigation.PageNavigationService.CreatePageFromSegment (System.String segment) [0x00023] in <039c2eaa586846c2bf4ee93ab79399dc>:0 . Priority: High. Timestamp:2018-04-28 22:07:46Z.
2018-04-28 22:07:47.035 DependencyApp.iOS[57378:1198089] *** Assertion failure in -[UIApplication _runWithMainScene:transitionContext:completion:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3698.52.10/UIApplication.m:3538
Unhandled Exception:

ILoggerFacadeを実装する

Prismが標準で用意しているログはDebugLoggerしかありませんが、例えばログをファイルに出力したい場合などは、別途自分でロガーを実装する必要があります。ILoggerFacadeは Log メソッドを一つだけ持つインターフェースなので、例えばNLogを利用してログを出力する場合はこんな感じで実装出来ます。

using NLog;
using Prism.Logging;

namespace DependencyApp.Prism.Logger
{
    public class NLogLogger : ILoggerFacade
    {
        private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

        private static LogLevel CategoryToLogLevel(Category category)
        {
            switch (category)
            {
                case Category.Debug:
                    return LogLevel.Debug;

                case Category.Exception:
                    return LogLevel.Error;

                case Category.Info:
                    return LogLevel.Info;

                case Category.Warn:
                    return LogLevel.Warn;

                default:
                    return LogLevel.Off;
            }
        }

        public void Log(string message, Category category, Priority priority)
        {
            Logger.Log(CategoryToLogLevel(category), message);
        }

    }
}