Apply Good Patterns about Logging in Executor Process

Posted on

Problem

I have source code for execute a command (ProcessStart) using Impersonate, and I want apply best practices and good design pattern, and good performance if possible.

I also want to use good Logging pattern. I use the Log pattern for logging my “Executor”.

Any suggestions?

Source code (partial)

 public partial class AgentServerExecutor
    {
        // TODO: Attention static multi-thread
        static StringBuilder sbLog = new StringBuilder();
        static Action<string> logTracer = null;
        private static void Log(string msg)
        {
            Trace.WriteLine(msg);
            sbLog.AppendLine(msg);
            if (logTracer == null) return;
            logTracer(msg);
        }

        public static bool DeployUsingCommandExecutor(Action<string> tracer, ArgumentsDeploymentExecutor arguments, out string errorDeploy)
        {
            logTracer = tracer;

            try
            {
                var command = arguments.ExecutorCommand;
                var empaquetadoXml = arguments.PackageXmlPath;
                var projectName = arguments.ProjectName;
                var projectGuid = arguments.ProjectGuid;

                Log("Deploying command " + command);

                var cmdDeploy = ConfigurationManager.AppSettings.Get(ConfigConsola.ComandoDeployKeyAppSettings);
                Log("Cmd: " + cmdDeploy);
                cmdDeploy = command;

                var f = new System.IO.FileInfo(cmdDeploy);
                Log("Cmd Fullname: " + f.FullName);
                if (!f.Exists) { errorDeploy = "Not exists " + f.FullName; return false; } // Logging ??

                Action<object, DataReceivedEventArgs> actionWrite = (sender, e) =>
                {
                    Log(" DataReceived " + e.Data);
                };

                Log("File: " + empaquetadoXml);
                GestorDatosDespliegue.FicheroDatosDespliegue = empaquetadoXml;
                GestorDatosDespliegue.CargarDespliegueActual();
                GestorDatosDespliegue.ObtenerListaDespliegues();
                List<DatosDespliegueBase> listaDatosDespliegue = GestorDatosDespliegue.ListaProyectos;

                Guid guid;
                var assertGuid = Guid.TryParse(projectGuid, out guid);
                if (!assertGuid) throw new InvalidOperationException("Guid no se considera válido: " + projectGuid);

                DatosDespliegueBase dd = null;
                foreach (var dd2 in listaDatosDespliegue)
                {
                    if (!(dd2.Name.Equals(projectName, StringComparison.InvariantCulture) && dd2.GuidProyecto.Equals(guid))) continue;
                    dd = dd2;
                }

                var ficheroSecurity = ConfigConsola.FicheroSecurity;
                Log("ficheroSecurity " + ficheroSecurity);
                Log("MapPath " + System.Web.Hosting.HostingEnvironment.MapPath("~/"));

                var rutaFicheroKey = System.Web.Hosting.HostingEnvironment.MapPath("~/" + ficheroSecurity);
                Log("rutaFicheroKey " + rutaFicheroKey);
                Log(System.Diagnostics.Process.GetCurrentProcess().ProcessName);
                if (AssemblyHelper.WebDevServerMode && !File.Exists(rutaFicheroKey))
                {
                    rutaFicheroKey = System.Web.Hosting.HostingEnvironment.MapPath("~/bin/" + ficheroSecurity);
                    Log("rutaFicheroKey " + rutaFicheroKey);
                }

                if (AssemblyHelper.UnitTestMode && !File.Exists(rutaFicheroKey))
                {
                    rutaFicheroKey = Path.Combine(Path.GetDirectoryName(AssemblyHelper.ApplicationLocation), ficheroSecurity);
                    Log("rutaFicheroKey " + rutaFicheroKey);
                }

                // TODO: If is Windows Service
                if (!File.Exists(rutaFicheroKey))
                {
                    rutaFicheroKey = Path.Combine(Path.GetDirectoryName(AssemblyHelper.ApplicationLocation), ficheroSecurity);
                    Log("rutaFicheroKey " + rutaFicheroKey);
                }

                var userPassDto = PasswordsManager.ObtenerUsuarioInstalador(dd.TipoAplicacionDespliegue, dd.EntornoActual.ToString(), rutaFicheroKey);
                if (!userPassDto.UsuarioObtenido) throw new InvalidOperationException("Not found credentials for " + dd.EntornoActual + " y Tipo aplicación " + dd.TipoAplicacionDespliegue);

                IssuesAboutSecurity();

                Log(
                    string.Format("•————————————————————————————————————————————————————————————————————————•")
                    + Environment.NewLine
                    + string.Format("| Credentials for deployment in  '{0}' environment        |", dd.EntornoActual)
                    + Environment.NewLine
                    + string.Format("| User '{0}' for DeploymentType '{1}' - '{2}'     |", userPassDto.Usuario, dd.TipoDespliegue, dd.TipoAplicacionDespliegue)
                    + Environment.NewLine
                    + string.Format("| User '{0}' - UserDomainName '{1}'                    |", Environment.UserName, Environment.UserDomainName)
                    + Environment.NewLine
                    + string.Format("| WindowsIdentity '{0}' |", WindowsIdentity.GetCurrent().Name)
                    + Environment.NewLine
                    + string.Format("•————————————————————————————————————————————————————————————————————————•")
                    + Environment.NewLine
                );

                //LocalSystem Usuario 'SYSTEM' UserDomainName 'MYDOMAIN'
                //LocalSystem WindowsIdentity NT AUTHORITYSYSTEM
                //LocalService | Usuario 'LOCAL SERVICE' - UserDomainName 'NT AUTHORITY'                    |
                //| WindowsIdentity 'NT AUTHORITYLOCAL SERVICE' |

                // Datos de usuario
                Log(string.Format("Actual User '{0}\{1}'. Client MachineName '{2}'", Environment.UserDomainName, Environment.UserName, Environment.MachineName));
                Log(string.Format("ProcessName '{0}'. CurrentProcess MachineName '{1}'rn", Process.GetCurrentProcess().ProcessName, Process.GetCurrentProcess().MachineName));

                var destino = Path.GetDirectoryName(empaquetadoXml);
               DeployManager.GenerarSustitucionesDeFicherosEnDirectorioDestino(destino, dd.EntornoActual, dd.SustitucionesPorFichero);

                string output = null, errors = null;
                int exitCode = int.MinValue;

                if (!Environment.MachineName.Equals(Environment.UserDomainName, StringComparison.InvariantCulture))
                {
                    var t1 = "MachineName " + Environment.MachineName + " NOT EQUALS UserDomainName " + Environment.UserDomainName
                    + Environment.NewLine;
                    Log(t1);
                    Log("Security Issues Asproc Library " + userPassDto.Usuario);
                    SecurityIssuesAsproLibrary(userPassDto.Usuario);

                    Log("Admin ??????" + userPassDto.Usuario);

                    //var u1 = new System.Security.Principal.WindowsIdentity(userPassDto.Usuario);
                    var u2 =  System.Security.Principal.WindowsIdentity.GetCurrent();
                    Log(" Admin " + u2.Name + " ? " + SecurityHelper.IsAdmin());

                    ActionHelper.SafeExecutor( () => {
                        string[] du = userPassDto.Usuario.Split('\');
                        Log(" Admin " + userPassDto.Usuario + " ? " + SecurityHelper.IsDomainAdmin(du[0], du[1])
                            + Environment.NewLine + " Validate " + SecurityHelper.ValidateCredentials(du[0], du[1], userPassDto.Password));

                    });
                }

                var logsFolder = Path.GetDirectoryName(empaquetadoXml).CombinePathWith("Logs");
                IOHelper.TryCreateDirectory(logsFolder);
                Log("Creado " + logsFolder);

                var args = arguments.ToArgumentsConsole();

                Action<ProcessStartInfo> actionForImpersonate = (startInfo) =>
                    {
                        var usuario = userPassDto.Usuario;
                        var pwd = userPassDto.Password;

                        bool usuarioConDominio = usuario.IndexOf("\") > -1;

                        var wi = WindowsIdentity.GetCurrent().Name;
                        usuarioConDominio = usuarioConDominio && !usuario.Equals(wi, StringComparison.InvariantCultureIgnoreCase);
                        Log("IMPERSONATE: User: " + usuario + Environment.NewLine
                            + "WindowsIdentity " + wi + Environment.NewLine);

                        string[] du = usuario.Split('\');
                        var codePwd = GetInfoPassword(pwd);

                        if (usuarioConDominio)
                        {
                            Log("IMPERSONATE: Usuario con dominio. Se procede a IMPERSONATE el proceso remoto con " + du[0] + "\" + du[1]);
                            Log(codePwd);

                            startInfo.Domain = du[0];
                            startInfo.UserName = du[1];
                            startInfo.Password = SecureStringHelper.ToSecureString(pwd);
                            startInfo.LoadUserProfile = true;
                        }

                        if (!usuarioConDominio)
                        {
                            Log("IMPERSONATE: Usuario SIN dominio. Se procede a IMPERSONATE el proceso remoto con " + Environment.MachineName + "\" + du[0]);
                            Log(codePwd);

                            startInfo.Domain = Environment.MachineName;
                            startInfo.UserName = du[0];
                            startInfo.Password = SecureStringHelper.ToSecureString(pwd);
                            startInfo.LoadUserProfile = true;
                        }
                    };

                ExecuteCommand(guid, f.FullName, args, actionWrite, actionForImpersonate, userPassDto.Usuario, userPassDto.Password, out output, out errors, out exitCode);

                Log("Output:rn" + output + "rn"
                    + "Errors:rn" + errors + "rn"
                    + "ExitCode: " + exitCode + "rn");

                var success = string.IsNullOrEmpty(errors)
                              && exitCode.Equals(0);
                errorDeploy = errors;
                return success;
            }
            finally
            {
                logTracer("*** LOG *** " + Environment.NewLine 
                + sbLog.ToString() + Environment.NewLine 
                + "*** LOG *** " + Environment.NewLine );
            }

        }

Solution

This is bad way to write code , I could not even read the code without headache. Please separate out your logging functionality, you function should not be more than 30 lines per method, so first

   
      Log(
                    string.Format("•————————————————————————————————————————————————————————————————————————•")
                    + Environment.NewLine
                    + string.Format("| Credentials for deployment in  '{0}' environment        |", dd.EntornoActual)
                    + Environment.NewLine
                    +
                    string.Format("| User '{0}' for DeploymentType '{1}' - '{2}'     |", userPassDto.Usuario,
                        dd.TipoDespliegue, dd.TipoAplicacionDespliegue)
                    + Environment.NewLine
                    +
                    string.Format("| User '{0}' - UserDomainName '{1}'                    |", Environment.UserName,
                        Environment.UserDomainName)
                    + Environment.NewLine
                    + string.Format("| WindowsIdentity '{0}' |", WindowsIdentity.GetCurrent().Name)
                    + Environment.NewLine
                    + string.Format("•————————————————————————————————————————————————————————————————————————•")
                    + Environment.NewLine
                    );
   
  

you dont need this whole code in this way , rather create a logger class and put this whole line of code there. Pass only dynamic parameter.

Once you are able to cleanup , pattern can be applied easily

Leave a Reply

Your email address will not be published. Required fields are marked *