Command writing to standard error does not produce output to ccnet project report? - cruisecontrol.net

I wrote a small c program like this:
#include<stdio.h>
#include<windows.h>
void main()
{
fputs("test\r\n", stderr);
fflush(stderr);
ExitProcess(-1);
}
and included it like this in ccnet.config:
<project name="foobar">
<tasks>
<exec>
<executable>C:\temp\test.exe</executable>
</exec>
</tasks>
</project>
When I forced the foobar project, I got not output whatsoever in the project report.
Why?
I may also mention that if I deliberately misspell the exe-filename, I get an exception (which I appreciate).
Edit:
When debugging CruiseControl.NET, writing a test that tries to run the exe file
that looks like this
[Test]
public void FooTest()
{
const string xml = "<exec executable=\"C:\\temp\\test.exe\"><buildArgs></buildArgs></exec>";
task = (ExecutableTask) NetReflector.Read(xml);
var result = (IntegrationResult) IntegrationResult();
result.Label = "1.0";
result.BuildCondition = BuildCondition.ForceBuild;
result.WorkingDirectory = #"c:\temp\";
result.ArtifactDirectory = #"c:\temp\";
task.Run(result);
Assert.AreEqual(IntegrationStatus.Failure, result.Status);
Assert.IsFalse(task.WasSuccessful);
var firstTaskResult = (ProcessTaskResult)result.TaskResults[0];
Debug.WriteLine(firstTaskResult.Data);
}
The debug console will output xml like this:
<buildresults>
<task>
<buildArgs />
<buildTimeoutSeconds>600</buildTimeoutSeconds>
<baseDirectory />
<dynamicValues />
<environment />
<executable>C:\temp\test.exe</executable>
<priority>Normal</priority>
<successExitCodes />
</task>
<message level="Error">test</message>
</buildresults>
(note, I did beautify this xml before adding it here).

Related

How to get the full path of current target file using NLog at runtime in .NET Core 7?

I have an application in .NET Core Console that is working fine in .NET Core 6. I am testing the conversion of this app in the newly released .NET Core 7 and all works fine except the part where I dynamically get the path of the NLog target.
My NLog.config is like this:
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Warn"
internalLogFile="internal-CimplDataLoader.log">
<extensions>
<add assembly="NLog.Extensions.Logging" />
</extensions>
<targets>
<target xsi:type="File" name="allfile" fileName="${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId.Id}|${logger}|${uppercase:${level}}|${message} ${exception}"
keepFileOpen="false"/>
</targets>
<rules>
<!--All logs, including from Microsoft-->
<logger name="*" minlevel="Trace" writeTo="allfile" />
</rules>
</nlog>
The code that works in .NET Core 6:
if (LogManager.Configuration != null)
{
Target target = LogManager.Configuration.FindTargetByName("allfile");
var logEventInfo = new LogEventInfo { TimeStamp = DateTime.Now };
FileTarget? fileTarget;
// Unwrap the target if necessary.
if (target is not WrapperTargetBase wrapperTarget)
fileTarget = target as FileTarget;
else
fileTarget = wrapperTarget.WrappedTarget as FileTarget;
if (fileTarget != null)
{
string fileName = fileTarget.FileName.Render(logEventInfo);
string LogPath = Path.GetDirectoryName(fileName)!;
/* Work with LogPath */
}
else
{
_logger.LogError("Unable to get NLog \"allfile\" base directory.");
}
}
else
{
_logger.LogError("Unable to read NLog configuration.");
}
This same code in .NET Core 7 fails because LogManager.Configuration is always null. Is there any way I can still dynamically get the path in .NET Core 7?
NLog.config was not present, hence the failure. This was due to the switch from .NET 6 to .NET 7 and nlog.config property "Copy To Output Directory" set to "Do not copy".

NLog MailTarget with bufferingwrapper and autoflushwrapper: reverse order of events?

I'm using the following structure:
<target name="MailOnError" xsi:type="AutoFlushWrapper" condition="level >= LogLevel.Error" flushOnConditionOnly="true">
<target name="MailBuffer" xsi:type="BufferingWrapper" bufferSize="250" overflowAction="Discard">
<-- note: mail config has been removed -->
<target xsi:type="Mail"
name="email"
header="Error in ${processname}:${newline}
Machine: ${machinename}${newline}
Proces: ${processname}${newline}
User: ${environment-user}${newline}
${newline}
${newline}"
layout="${date:format=HH\:mm\:ss}: ${message} (in ${callsite})${newline}"
/>
</target>
This works almost fine to achieve my goal: receiving an e-mail on error, with the most recent log-entries embedded to have some context about the error.
It would be nice if I get the error on the top of my e-mail-message (or, even better, receive the log-entries in descending order).
How can this be done (if possible without writing my own custom target)?
You need something like this:
[Target("ReverseOrderWrapper", IsWrapper = true)]
public class ReverseOrderWrapper : WrapperTargetBase
{
protected override void Write(IList<AsyncLogEventInfo> logEvents)
{
// Some re oder logic
var reorderEvents = logEvents.Reverse().ToArray();
WrappedTarget.WriteAsyncLogEvents(reorderEvents);
}
}
Register (as soon as possible), NLog 4.7+ syntax
NLog.LogManager.Setup().SetupExtensions(s =>
s.RegisterTarget<ReverseOrderWrapper>("ReverseOrderWrapper")
);
Usage:
<target name="MailOnError" xsi:type="AutoFlushWrapper" condition="level >= LogLevel.Error" flushOnConditionOnly="true">
<target name="MailBuffer" xsi:type="BufferingWrapper" bufferSize="250"
overflowAction="Discard">
<target name="Reoder" xsi:type="ReverseOrderWrapper">
<-- note: mail config has been removed -->
<target xsi:type="Mail"
name="email"
header="Error in ${processname}:${newline}
Machine: ${machinename}${newline}
Proces: ${processname}${newline}
User: ${environment-user}${newline}
${newline}
${newline}"
layout="${date:format=HH\:mm\:ss}: ${message} (in ${callsite})${newline}"
/>
</target>
</target>
</target>

How to share audio files in Android Studio (Kotlin)?

How can I share an audio file in Android Studio? I have tried the following so far, but it doesn't work.
button.setOnLongClickListener(OnLongClickListener {
val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
var path = "absolute/path"
var uri = Uri.parse(path)
putExtra(Intent.EXTRA_STREAM, uri)
type = "audio/mp3"
}
startActivity(sendIntent)
true
})
Your code seems fine, but I'd suggest you to try this one:
String sharePath = Environment.getExternalStorageDirectory().getPath()
+ "/Soundboard/Ringtones/custom_ringtone.ogg"; //This is the path of your audio file
Uri uri = Uri.parse(sharePath); //Identifier of the audio file (Uniform Resource Identifier)
Intent share = new Intent(Intent.ACTION_SEND); //Create a new action_send intent
share.setType("audio/*"); //What kind of file the intent gets
share.putExtra(Intent.EXTRA_STREAM, uri); //Pass the audio file to the intent
startActivity(Intent.createChooser(share, "Share Sound File")); //Start the intent
If that doesn't work either, make sure that you granted the right permissions in the Manifest.xml file (WRITE_EXTERNAL_STORAGE):
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
I suggest you look into how to use a file provider. You'll find that you won't be able to provide the uri of the file directly as you're only allowed to expose its content uri. Have a look at the Generating the Content URI for a File section
This is worked for me
Call this method onClick Button:
fun shareAudioFile(audioFile: File) {
val uri = FileProvider.getUriForFile(applicationContext,
"com.app.package.fileprovider",
File(audioFile)
val shareIntent: Intent = ShareCompat.IntentBuilder.from(this#MainActivity)
.setType("audio/mp3")
.setStream(uri)
.intent
startActivity(Intent.createChooser(shareIntent, "Share Sound File"))
}
I replaced Uri.parse with FileProvider.getUriForFile.
Replace com.app.package with your package.
Add on Android Manifest:
<application ...>
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"/>
</provider>
<application/>
applicationId is your package
Create file_paths.xml file into xml folder
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external_files"
path="." />
</paths>
Share Any File as below ( Kotlin ) :
first create a folder named xml in the res folder and create a new XML Resource File named provider_paths.xml and put the below code inside it :
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path
name="files"
path="."/>
<external-path
name="external_files"
path="."/>
</paths>
now go to the manifests folder and open the AndroidManifest.xml and then put the below code inside the <application> tag :
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" /> // provider_paths.xml file path in this example
</provider>
now you put the below code in the setOnLongClickListener :
button.setOnLongClickListener {
try {
val file = File("pathOfFile")
if(file.exists()) {
val uri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", file)
val intent = Intent(Intent.ACTION_SEND)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.setType("*/*")
intent.putExtra(Intent.EXTRA_STREAM, uri)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent)
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
toast("Error")
}
}

WCF MaxItemsInObjectGraph setting not working

I have been getting the following error trying to access my WCF service.
'Maximum number of items that can be serialized or deserialized in an object graph is '65536'. Change the object graph or increase the MaxItemsInObjectGraph quota
Doing some research, it looks like all I need to do is update this setting to be a higher value. This is what I am trying to do, but the setting does not seem to be getting read from the configuration. I keep getting the same exception with the 65536 value in it.
I followed the instructions found at this Link, but am having no luck.
Here is what I have configured on the WCF Service's Web.Config.
<behaviors>
<serviceBehaviors>
<behavior name="metadataBehavior">
<serviceMetadata httpGetEnabled="true" httpGetUrl="" />
<serviceDebug includeExceptionDetailInFaults="false" />
<dataContractSerializer maxItemsInObjectGraph="2147483646"/>
</behavior>
</serviceBehaviors>
</behaviors>
This is what is in the Client's app.config:
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True" />
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior >
<dataContractSerializer maxItemsInObjectGraph="2147483646"/>
</behavior>
</endpointBehaviors>
</behaviors>
And lastly, I have the following attribute on the WCF service itself:
[ServiceBehavior(MaxItemsInObjectGraph = 2147483646, IncludeExceptionDetailInFaults = true)]
Despite the configurations above, I still get an Exception complaining about the 65536 value. Why aren't any of these settings being used by the applications? Is there something else that needs to be set somewhere?
You were on the right track!
All you had to do was add a name to the behavior
<behavior name="MyBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483646"/>
</behavior>
And then on the end point add
<endpoint .... behaviorConfiguration="MyBehavior"/>
Had to go nuclear and update that machine.config;
Directions Here
The gist of it is to add the following to the "system.serviceModel" section.
<commonBehaviors>
<endpointBehaviors>
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</endpointBehaviors>
<serviceBehaviors>
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</serviceBehaviors>
</commonBehaviors>
I wrote a program to modify the machine configs for this, because support. It works for me, but I haven't done tons of testing.
using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace FixMachineConfigBehavior
{
class Program
{
public static XElement IfNotExistsAdd(XDocument xd, XElement rootElement, string childName, XElement newChild)
{
if (rootElement.Elements(childName).Count() == 0)
{
Console.WriteLine(" adding " + childName + " node...");
rootElement.Add(newChild);
}
return rootElement.Element(childName);
}
static void Main(string[] args)
{
foreach (var file in Directory.EnumerateFiles(Environment.GetEnvironmentVariable("windir") + #"\Microsoft.NET\","machine.config",SearchOption.AllDirectories))
{
Console.WriteLine("fixing: " + file);
TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1);
double ms = t.TotalMilliseconds;
File.Copy(file, file + "." + ms + ".bak", true);
var xd = XDocument.Load(file);
XElement i = xd.Root;
i = IfNotExistsAdd(xd, i, "system.serviceModel", new XElement("system.serviceModel"));
i = IfNotExistsAdd(xd, i, "commonBehaviors", new XElement("commonBehaviors"));
i = IfNotExistsAdd(xd, i, "endpointBehaviors", new XElement("endpointBehaviors"));
i = IfNotExistsAdd(xd, i, "dataContractSerializer", new XElement("dataContractSerializer", new XAttribute("maxItemsInObjectGraph", Int32.MaxValue)));
xd.Save(file);
}
Console.ReadLine();
}
}
}
I had the same problem and tried several options but I found the solution here:
https://msdn.microsoft.com/en-us/library/ms732038.aspx
In "Controlling the serialization process".
Adding ...
[ServiceBehavior(MaxItemsInObjectGraph=100000)]
class My Service ...
good luck
I had the same issue , There was some enums in returning class. What found out they cannot be null. Check whether you have any Enums that are to be returned.

log4net MemoryAppender not working

I'm using log4net to log in my app. My FileAppender is working fine, but I'm having problems with MemoryAppender.
Here is my config file.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<appender name="LogFileAppender" type="log4net.Appender.FileAppender">
<param name="File" value="Envision.log" />
<param name="AppendToFile" value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="Header" value="" />
<param name="Footer" value="" />
<param name="ConversionPattern" value="%d [%t] %-5p %m%n" />
</layout>
</appender>
<appender name="MemoryAppender" type="log4net.Appender.MemoryAppender">
</appender>
<root>
<level value="ALL" />
<appender-ref ref="LogFileAppender" />
<appender-ref ref="MemoryAppender" />
</root>
</log4net>
</configuration>
I use this code to setup the config file.
FileInfo file = new FileInfo(configPath);
log4net.Config.XmlConfigurator.Configure(file);
file = null;
Like I said, the FileAppender works great. But I can't seem to get any events.
I've tried using something like this to get the MemoryAppender.
Hierarchy hierarchy = LogManager.GetRepository() as Hierarchy;
MemoryAppender mappender = hierarchy.Root.GetAppender("MemoryAppender") as MemoryAppender;
I've tried using:
var events = mappender.GetEvents()
after logging something, and events is always empty. I've tried setting up the FileAppender and MemoryAppender in code instead of using the config file, and I get the same, the FileAppender works fine, but can't seem to get any events from MemoryAppender. Curious if I'm understanding MemoryAppender right? I also tried setting up a thread that loops checking for the GetEvents to not be empty, and while logging away it always comes back empty. I've tried setting the Threshold to Core.Level.All on the MemoryAppender but that did not change anything.
Thanks for any direction. I've looked around, and from the sites I've seen, I can't tell what I'm doing different.
Even something as simple as this does not work. events length is always zero;
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
MemoryAppender appender = new MemoryAppender();
ILog logger = LogManager.GetLogger("foo");
BasicConfigurator.Configure(appender);
logger.Error("Should work");
var events = appender.GetEvents();
}
}
For those that need it, here's how to do it programmatically in C#:
var memoryAppender = new MemoryAppender();
var repository = (log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository();
repository.Root.AddAppender(memoryAppender);
var events = memoryAppender.GetEvents();
I used Ralph's code above in my unit testing:
using log4net;
using log4net.Appender;
// ...
internal static MemoryAppender GetMemoLog<T>() where T: class
{
var memoLog = new MemoryAppender();
ILog appendableLog = LogManager.GetLogger(typeof(T).Assembly, typeof(T));
var repository = (log4net.Repository.Hierarchy.Hierarchy)appendableLog.Logger.Repository;
repository.Root.AddAppender(memoLog);
var logField = typeof(T).GetField("Log", BindingFlags.Static | BindingFlags.NonPublic);
if (logField != null) logField.SetValue(null, appendableLog);
return memoLog;
}
This assumes you have a private static Log field on your class:
private static readonly ILog Log = LogManager.GetLogger(typeof(MyClass));
So, in the test, it's just:
var memoLog = GetMemoLog<MyClass>();
// followed by test logic, and then...
var events = memoLog.GetEvents();
The simple sample code you posted works fine for me using log4net 1.2.10.0.
I would recommend downloading the source and stepping through it in a debugger. It may seem a little daunting at first, but you get used to their code pretty quickly and it's not hard to follow. I've done this many times when I had problems with custom constraints and appenders. It really helps solve problems quickly and gives you a much better understanding of how log4net works.
I figured it out. I was using the Compact Framework .dll by mistake. Once I realized that I switched to the .net 2.0 version, which caused a problem with log4net namespace not being found, so I did a search on that and realized I needed to change my .net Framework 4 client Profile to .net Framework 4. I'm now getting the events as expected.
I adapted CZahrobsky's answer. Had to tweak slightly, since my class cannot have static logger by design.
Class under test has the log field declared like:
private ILog Logger = Log4netFactory.GetLogger(typeof(MyClass));
In the GetMemLog logic I have to first create an instance of MyClass and change the logField look up to getField by name 'Logger' and BindingFlags.Instance instead of BindingFlags.Static
//create an instance of the class
var myObject = new MyClass(context);
var memoryLog = new MemoryAppender();
ILog appendableLog = LogManager.GetLogger(typeof(JobQueue).Assembly, typeof(MyClass));
var repository = (log4net.Repository.Hierarchy.Hierarchy)appendableLog.Logger.Repository;
repository.Root.AddAppender(memoryLog);
var logField = typeof(MyClass).GetField("Logger", BindingFlags.NonPublic | BindingFlags.Instance);
if (logField != null)
{
//set logfield property value for the instance
logField.SetValue(myObject, appendableLog);
}
Examples on SetValue() for PropertyInfo is here

Resources