Postscript after writing this post: I thought I found a bug in Echo and Print. But in fact it was my mistake to misunderstand "Row".
In all my programs I use a global variable testLvl with which I can control echo outputs. I.e.: I can write:
testLvl = 3;
(* ... *)
If[testLvl >= 3, Echo[expr1, label1]];
If[testLvl >= 4, Echo[expr2, label2]];

To shorten this further, I have my own echo routine that essentially includes this query of testLvl:
ClearAll[echo];
echo[expr_, label_ : "", lvl0_Integer : 3] :=
If[testLvl >= lvl0,
Echo[expr, Row[{{testLvl, lvl0}, ": ", label}]]
, expr];
testLvl = 3;
(* ... *)
echo[expr1, label1];
echo[expr2, label2, 4];
testLvl = 4;
(* ... *)
echo[expr1, label1];
echo[expr2, label2, 4];

In doing so, I ran into the following problem within my echo routine:
The label in Echo[expr, label] should be any expression. According to the documentation it does not have to be a String. Therefore, it could be any Row as an example.
This works:
Echo[E, "1.: E - Label as a String: "];
Echo[E, Row[{2, ".: E", " - Label as a Row: "}]]

If newlines appear in a String label, Echo still works fine (except for the indentation of the expression):
Echo[N[E], "3.: N[E]\nLabel as a String:\n"]

But when newlines occur in a non-string label, say a Row label, a problem occurs. The label is split and the expression appears somewhere in between:
Echo[N[E], Row[{4, ".: N[E]\nLabel as a Row:\n"}]]

I was afraid this is a Print bug. In any case, Print shows the same (assumed mis-) behavior:
Print[Row[{5, ".: N[E]\nLabel as a Row:\n"}], N[E]]

It occurs when Print outputs not only a pure Row or a sequence of expressions. Otherwise everything is fine:
Print[6, ".: N[E]\nLabel as a String:\n", N[E]];
Print[Row[{7, ".: N[E]\nLabel as a String:\n", N[E]}]];

Print's documentation says that Print[expr1,expr2,...] concatenates the expressions and effectively uses Row. I.e.: Print[Row[{expr1,expr2,...}]] should output the same. This remains true if one of the expressions is itself a Row, but the result seems wrong at first sight:
Print[Row[{8, "a.: N[E],\nLabel as a String:", "\n"}], N[E]];
Print[Row[{Row[{8, "b.: N[E],\nLabel as a String:", "\n"}], N[E]}]];

It wasn't until I was writing this post that I realized I had misunderstood "Row" in this context, since Row exhibits the same behavior. The elements of Row are not simply concatenated like in a typewriter, but each part is formatted separately in StandardForm (line wrapping if necessary) and the individual results are concatenated:
Row[{Row[{8, "bR.: N[E],\nLabel as a String:", "\n"}], N[E]}]

Going back to my original problem (see example 4 above) you just have to convert the label to a String to fix it. Of course, you can then no longer place arbitrary expressions in the label, but only those that can be meaningfully converted to a string. To solve our newline problem, it is better to use EchoFunction when the label ends with a newline:
Echo[N[E], Row[{4, ".: N[E]\nLabel as a Row:\n"}]];
Echo[N[E],
ToString@Row[{9,
". = 4.Fixed: N[E]\nLabel as a Row converted to String:\n"}]];
EchoFunction[
Row[{Style[
Row[{10, ". = 4.Fixed: N[E]\nLabel as a Row echoed with \
EchoFunction:"}], "EchoLabel"],
"\n", #}] &][N[E]];

Because it is difficult to determine in the general case whether a label ends with a newline, I make a new function echoNl that brings the expression under the label:
ClearAll[echoNl];
echoNl[expr_, label_ : "", lvl0_Integer : 3] := Module[{myLbl},
If[testLvl >= lvl0,
myLbl = Row[{{testLvl, lvl0}, ": ", label}];
EchoFunction[Row[{Style[myLbl, "EchoLabel"], "\n", #}] &][expr]
, expr]
];
testLvl = 3;
(* ... *)
echoNl[expr1, label1];
echo[expr2, label2, 4];
testLvl = 4;
(* ... *)
echoNl[expr1, label1];
echo[expr2, label2, 4];

This is my final solution.
Attachments: